mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 12:10:41 -05:00
Merge remote-tracking branch 'upstream/develop' into user-profile-info
This commit is contained in:
commit
cdaddbb44f
6
.env.ci
6
.env.ci
@ -9,13 +9,13 @@ LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=33306
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=testing
|
||||
DB_USERNAME=user
|
||||
DB_PASSWORD=password
|
||||
|
||||
SCOUT_DRIVER=null
|
||||
SCOUT_DRIVER=collection
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
CACHE_STORE=array
|
||||
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
@ -4,7 +4,7 @@ on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
security-checker:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@ -12,66 +12,79 @@ jobs:
|
||||
uses: symfonycorp/security-checker-action@v5
|
||||
|
||||
larastan:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
||||
|
||||
- name: Prepare Laravel Environment
|
||||
run: |
|
||||
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
||||
php artisan key:generate
|
||||
php artisan optimize
|
||||
|
||||
- name: Execute Code Static Analysis with Larastan
|
||||
run: ./vendor/bin/phpstan analyse -c ./phpstan.neon --no-progress --error-format=github
|
||||
|
||||
pint-fixer:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
||||
|
||||
- name: Prepare Laravel Environment
|
||||
run: |
|
||||
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
||||
php artisan key:generate
|
||||
php artisan optimize
|
||||
|
||||
- name: Run Pint Code Style Fixer
|
||||
run: ./vendor/bin/pint
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Pint PHP Style Fixes [no ci]
|
@ -4,7 +4,7 @@ on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
laravel-tests:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.3
|
||||
@ -13,53 +13,65 @@ jobs:
|
||||
MYSQL_USER: user
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
ports:
|
||||
- 33306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
options: --health-cmd="mysql -u user -D testing -ppassword -h mysql -e ''" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Get Composer Cache Directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
||||
|
||||
- name: Get NPM Cache Directory
|
||||
id: npm-cache
|
||||
run: echo "NPM_CACHE_DIR=$(npm config get cache)" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache NPM Dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.NPM_CACHE_DIR }}
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: ${{ runner.os }}-node-
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build Front-end Assets
|
||||
run: npm run build
|
||||
|
||||
- name: Prepare Laravel Environment
|
||||
run: |
|
||||
php -r "file_exists('.env') || copy('.env.ci', '.env');"
|
||||
php artisan key:generate
|
||||
php artisan optimize
|
||||
|
||||
- name: Run Database Migrations
|
||||
run: php artisan migrate
|
||||
|
||||
- name: Link Storage
|
||||
run: php artisan storage:link
|
||||
|
||||
- name: Run Tests
|
||||
run: php artisan test
|
||||
|
||||
- name: Display Laravel Log
|
||||
if: failure()
|
||||
run: cat storage/logs/laravel.log
|
40
.github/dependabot.yml
vendored
40
.github/dependabot.yml
vendored
@ -1,40 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Composer dependencies (PHP)
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "15:00" # 10am EST
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: "develop"
|
||||
labels:
|
||||
- "dependencies"
|
||||
assignees:
|
||||
- "Refringe"
|
||||
|
||||
# GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "15:00" # 10am EST
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: "develop"
|
||||
labels:
|
||||
- "dependencies"
|
||||
assignees:
|
||||
- "Refringe"
|
||||
|
||||
# npm modules (JavaScript)
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "15:00" # 10am EST
|
||||
open-pull-requests-limit: 10
|
||||
target-branch: "develop"
|
||||
labels:
|
||||
- "dependencies"
|
||||
assignees:
|
||||
- "Refringe"
|
@ -4,16 +4,24 @@ namespace App\Http\Controllers\Api\V0;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Str;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
class ApiController extends Controller
|
||||
{
|
||||
/**
|
||||
* Determine if the given relationship should be included in the request. If more than one relationship is provided,
|
||||
* only one needs to be present in the request for this method to return true.
|
||||
*
|
||||
* @param string|string[] $relationships
|
||||
*/
|
||||
public static function shouldInclude(string|array $relationships): bool
|
||||
{
|
||||
$param = request()->get('include');
|
||||
try {
|
||||
$param = request()->get('include');
|
||||
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $param) {
|
||||
return false;
|
||||
|
@ -7,13 +7,15 @@ use App\Http\Requests\Api\V0\StoreModRequest;
|
||||
use App\Http\Requests\Api\V0\UpdateModRequest;
|
||||
use App\Http\Resources\Api\V0\ModResource;
|
||||
use App\Models\Mod;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ModController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(ModFilter $filters)
|
||||
public function index(ModFilter $filters): AnonymousResourceCollection
|
||||
{
|
||||
return ModResource::collection(Mod::filter($filters)->paginate());
|
||||
}
|
||||
@ -21,15 +23,12 @@ class ModController extends ApiController
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(StoreModRequest $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function store(StoreModRequest $request): void {}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Mod $mod)
|
||||
public function show(Mod $mod): JsonResource
|
||||
{
|
||||
return new ModResource($mod);
|
||||
}
|
||||
@ -37,16 +36,10 @@ class ModController extends ApiController
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(UpdateModRequest $request, Mod $mod)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function update(UpdateModRequest $request, Mod $mod): void {}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Mod $mod)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function destroy(Mod $mod): void {}
|
||||
}
|
||||
|
@ -7,13 +7,15 @@ use App\Http\Requests\Api\V0\StoreUserRequest;
|
||||
use App\Http\Requests\Api\V0\UpdateUserRequest;
|
||||
use App\Http\Resources\Api\V0\UserResource;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UsersController extends ApiController
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(UserFilter $filters)
|
||||
public function index(UserFilter $filters): AnonymousResourceCollection
|
||||
{
|
||||
return UserResource::collection(User::filter($filters)->paginate());
|
||||
}
|
||||
@ -21,15 +23,12 @@ class UsersController extends ApiController
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(StoreUserRequest $request)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function store(StoreUserRequest $request): void {}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(User $user)
|
||||
public function show(User $user): JsonResource
|
||||
{
|
||||
return new UserResource($user);
|
||||
}
|
||||
@ -37,16 +36,10 @@ class UsersController extends ApiController
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function update(UpdateUserRequest $request, User $user): void {}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
public function destroy(User $user): void {}
|
||||
}
|
||||
|
@ -6,26 +6,27 @@ use App\Http\Requests\ModRequest;
|
||||
use App\Http\Resources\ModResource;
|
||||
use App\Models\Mod;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ModController extends Controller
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function index()
|
||||
public function index(): View
|
||||
{
|
||||
$this->authorize('viewAny', Mod::class);
|
||||
|
||||
return view('mod.index');
|
||||
}
|
||||
|
||||
public function store(ModRequest $request)
|
||||
public function store(ModRequest $request): ModResource
|
||||
{
|
||||
$this->authorize('create', Mod::class);
|
||||
|
||||
return new ModResource(Mod::create($request->validated()));
|
||||
}
|
||||
|
||||
public function show(int $modId, string $slug)
|
||||
public function show(int $modId, string $slug): View
|
||||
{
|
||||
$mod = Mod::with([
|
||||
'versions',
|
||||
@ -47,7 +48,7 @@ class ModController extends Controller
|
||||
return view('mod.show', compact(['mod']));
|
||||
}
|
||||
|
||||
public function update(ModRequest $request, Mod $mod)
|
||||
public function update(ModRequest $request, Mod $mod): ModResource
|
||||
{
|
||||
$this->authorize('update', $mod);
|
||||
|
||||
@ -56,12 +57,5 @@ class ModController extends Controller
|
||||
return new ModResource($mod);
|
||||
}
|
||||
|
||||
public function destroy(Mod $mod)
|
||||
{
|
||||
$this->authorize('delete', $mod);
|
||||
|
||||
$mod->delete();
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
public function destroy(Mod $mod): void {}
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ namespace App\Http\Controllers;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function show(Request $request, User $user, string $username)
|
||||
public function show(Request $request, User $user, string $username): View
|
||||
{
|
||||
if ($user->slug() !== $username) {
|
||||
abort(404);
|
||||
|
@ -11,14 +11,21 @@ class ModFilter
|
||||
{
|
||||
/**
|
||||
* The query builder instance for the mod model.
|
||||
*
|
||||
* @var Builder<Mod>
|
||||
*/
|
||||
protected Builder $builder;
|
||||
|
||||
/**
|
||||
* The filter that should be applied to the query.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected array $filters;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $filters
|
||||
*/
|
||||
public function __construct(array $filters)
|
||||
{
|
||||
$this->builder = $this->baseQuery();
|
||||
@ -27,6 +34,8 @@ class ModFilter
|
||||
|
||||
/**
|
||||
* The base query for the mod listing.
|
||||
*
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
private function baseQuery(): Builder
|
||||
{
|
||||
@ -49,6 +58,8 @@ class ModFilter
|
||||
|
||||
/**
|
||||
* Apply the filters to the query.
|
||||
*
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
public function apply(): Builder
|
||||
{
|
||||
@ -58,13 +69,13 @@ class ModFilter
|
||||
}
|
||||
}
|
||||
|
||||
//dd($this->builder->toRawSql());
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the query by the given type.
|
||||
*
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
private function order(string $type): Builder
|
||||
{
|
||||
@ -92,6 +103,8 @@ class ModFilter
|
||||
|
||||
/**
|
||||
* Filter the results by the given search term.
|
||||
*
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
private function query(string $term): Builder
|
||||
{
|
||||
@ -100,6 +113,8 @@ class ModFilter
|
||||
|
||||
/**
|
||||
* Filter the results by the featured status.
|
||||
*
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
private function featured(string $option): Builder
|
||||
{
|
||||
@ -112,6 +127,9 @@ class ModFilter
|
||||
|
||||
/**
|
||||
* Filter the results to specific SPT versions.
|
||||
*
|
||||
* @param array<int, string> $versions
|
||||
* @return Builder<Mod>
|
||||
*/
|
||||
private function sptVersions(array $versions): Builder
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ class License extends Model
|
||||
*/
|
||||
public function mods(): HasMany
|
||||
{
|
||||
return $this->hasMany(Mod::class);
|
||||
return $this->hasMany(Mod::class)
|
||||
->chaperone();
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,8 @@ class Mod extends Model
|
||||
{
|
||||
return $this->hasMany(ModVersion::class)
|
||||
->whereHas('latestSptVersion')
|
||||
->orderByDesc('version');
|
||||
->orderByDesc('version')
|
||||
->chaperone();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,7 +83,8 @@ class Mod extends Model
|
||||
{
|
||||
return $this->hasOne(ModVersion::class)
|
||||
->whereHas('latestSptVersion')
|
||||
->orderByDesc('updated_at');
|
||||
->orderByDesc('updated_at')
|
||||
->chaperone();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,7 +116,8 @@ class Mod extends Model
|
||||
->whereHas('sptVersions')
|
||||
->orderByDesc('version')
|
||||
->orderByDesc('updated_at')
|
||||
->take(1);
|
||||
->take(1)
|
||||
->chaperone();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,7 +31,8 @@ class ModDependency extends Model
|
||||
*/
|
||||
public function resolvedDependencies(): HasMany
|
||||
{
|
||||
return $this->hasMany(ModResolvedDependency::class, 'dependency_id');
|
||||
return $this->hasMany(ModResolvedDependency::class, 'dependency_id')
|
||||
->chaperone();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,7 +42,8 @@ class ModVersion extends Model
|
||||
*/
|
||||
public function dependencies(): HasMany
|
||||
{
|
||||
return $this->hasMany(ModDependency::class);
|
||||
return $this->hasMany(ModDependency::class)
|
||||
->chaperone();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,19 +2,24 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class UserRole extends Model
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The relationship between a user role and users.
|
||||
*
|
||||
* @return HasMany<User>
|
||||
*/
|
||||
public function users(): HasMany
|
||||
{
|
||||
return $this->hasMany(User::class);
|
||||
return $this->hasMany(User::class)
|
||||
->chaperone();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ class ResetPassword extends OriginalResetPassword implements ShouldQueue
|
||||
parent::__construct($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [];
|
||||
|
@ -13,6 +13,11 @@ class VerifyEmail extends OriginalVerifyEmail implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function toArray(object $notifiable): array
|
||||
{
|
||||
return [];
|
||||
|
@ -18,6 +18,8 @@ class DependencyVersionService
|
||||
|
||||
/**
|
||||
* Satisfies all dependency constraints of a ModVersion.
|
||||
*
|
||||
* @return array<int, array<string, int>>
|
||||
*/
|
||||
private function satisfyConstraint(ModVersion $modVersion): array
|
||||
{
|
||||
|
@ -19,6 +19,8 @@ class SptVersionService
|
||||
|
||||
/**
|
||||
* Satisfies the version constraint of a given ModVersion. Returns the ID of the satisfying SptVersion.
|
||||
*
|
||||
* @return array<int>
|
||||
*/
|
||||
private function satisfyConstraint(ModVersion $modVersion): array
|
||||
{
|
||||
|
@ -6,11 +6,21 @@ use Illuminate\Http\JsonResponse;
|
||||
|
||||
trait ApiResponses
|
||||
{
|
||||
/**
|
||||
* Return a success JSON response.
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
protected function success(string $message, ?array $data = []): JsonResponse
|
||||
{
|
||||
return $this->baseResponse(message: $message, data: $data, code: 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base response.
|
||||
*
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
private function baseResponse(?string $message = '', ?array $data = [], ?int $code = 200): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
@ -19,6 +29,9 @@ trait ApiResponses
|
||||
], $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error JSON response.
|
||||
*/
|
||||
protected function error(string $message, int $code): JsonResponse
|
||||
{
|
||||
return $this->baseResponse(message: $message, code: $code);
|
||||
|
@ -11,7 +11,7 @@ trait HasCoverPhoto
|
||||
/**
|
||||
* Update the user's cover photo.
|
||||
*/
|
||||
public function updateCoverPhoto(UploadedFile $cover, $storagePath = 'cover-photos'): void
|
||||
public function updateCoverPhoto(UploadedFile $cover, string $storagePath = 'cover-photos'): void
|
||||
{
|
||||
tap($this->cover_photo_path, function ($previous) use ($cover, $storagePath) {
|
||||
$this->forceFill([
|
||||
@ -51,15 +51,17 @@ trait HasCoverPhoto
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to the user's cover photo.
|
||||
* Get the cover photo URL for the user.
|
||||
*
|
||||
* @return Attribute<string, never>
|
||||
*/
|
||||
public function coverPhotoUrl(): Attribute
|
||||
{
|
||||
return Attribute::get(function (): string {
|
||||
return $this->cover_photo_path
|
||||
return new Attribute(
|
||||
get: fn (): string => $this->cover_photo_path
|
||||
? Storage::disk($this->coverPhotoDisk())->url($this->cover_photo_path)
|
||||
: $this->defaultCoverPhotoUrl();
|
||||
});
|
||||
: $this->defaultCoverPhotoUrl()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,17 +2,28 @@
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Models\Mod;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class ModList extends Component
|
||||
{
|
||||
/**
|
||||
* The mods to display.
|
||||
*
|
||||
* @var Collection<int, Mod>
|
||||
*/
|
||||
public Collection $mods;
|
||||
|
||||
public string $versionScope;
|
||||
|
||||
public function __construct($mods, $versionScope)
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @param Collection<int, Mod> $mods
|
||||
*/
|
||||
public function __construct(Collection $mods, string $versionScope)
|
||||
{
|
||||
$this->mods = $mods;
|
||||
$this->versionScope = $versionScope;
|
||||
|
@ -11,10 +11,25 @@ use Illuminate\View\Component;
|
||||
|
||||
class ModListSection extends Component
|
||||
{
|
||||
/**
|
||||
* The featured mods listed on the homepage.
|
||||
*
|
||||
* @var Collection<int, Mod>
|
||||
*/
|
||||
public Collection $modsFeatured;
|
||||
|
||||
/**
|
||||
* The latest mods listed on the homepage.
|
||||
*
|
||||
* @var Collection<int, Mod>
|
||||
*/
|
||||
public Collection $modsLatest;
|
||||
|
||||
/**
|
||||
* The last updated mods listed on the homepage.
|
||||
*
|
||||
* @var Collection<int, Mod>
|
||||
*/
|
||||
public Collection $modsUpdated;
|
||||
|
||||
public function __construct()
|
||||
@ -24,6 +39,11 @@ class ModListSection extends Component
|
||||
$this->modsUpdated = $this->fetchUpdatedMods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the featured mods homepage listing.
|
||||
*
|
||||
* @return Collection<int, Mod>
|
||||
*/
|
||||
private function fetchFeaturedMods(): Collection
|
||||
{
|
||||
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads'])
|
||||
@ -39,6 +59,11 @@ class ModListSection extends Component
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the latest mods homepage listing.
|
||||
*
|
||||
* @return Collection<int, Mod>
|
||||
*/
|
||||
private function fetchLatestMods(): Collection
|
||||
{
|
||||
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at', 'downloads'])
|
||||
@ -53,6 +78,11 @@ class ModListSection extends Component
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the recently updated mods homepage listing.
|
||||
*
|
||||
* @return Collection<int, Mod>
|
||||
*/
|
||||
private function fetchUpdatedMods(): Collection
|
||||
{
|
||||
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads'])
|
||||
@ -81,6 +111,11 @@ class ModListSection extends Component
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the sections for the homepage mod lists.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function getSections(): array
|
||||
{
|
||||
return [
|
||||
|
@ -2,14 +2,16 @@
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Models\Mod;
|
||||
use App\Models\ModVersion;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class ModListStats extends Component
|
||||
{
|
||||
public function __construct(
|
||||
public $mod,
|
||||
public $modVersion
|
||||
public Mod $mod,
|
||||
public ModVersion $modVersion
|
||||
) {}
|
||||
|
||||
public function render(): View
|
||||
|
@ -6,6 +6,9 @@ use App\Models\License;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Factory<License>
|
||||
*/
|
||||
class LicenseFactory extends Factory
|
||||
{
|
||||
protected $model = License::class;
|
||||
|
@ -8,6 +8,9 @@ use App\Models\ModVersion;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Factory<ModDependency>
|
||||
*/
|
||||
class ModDependencyFactory extends Factory
|
||||
{
|
||||
protected $model = ModDependency::class;
|
||||
|
@ -7,25 +7,23 @@ use App\Models\Mod;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
use Random\RandomException;
|
||||
|
||||
/**
|
||||
* @extends Factory<Mod>
|
||||
*/
|
||||
class ModFactory extends Factory
|
||||
{
|
||||
protected $model = Mod::class;
|
||||
|
||||
/**
|
||||
* @throws RandomException
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
|
||||
$name = fake()->catchPhrase();
|
||||
$name = fake()->sentence(rand(3, 5));
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
'slug' => Str::slug($name),
|
||||
'teaser' => fake()->sentence(),
|
||||
'description' => fake()->paragraphs(random_int(4, 20), true),
|
||||
'description' => fake()->paragraphs(rand(4, 20), true),
|
||||
'license_id' => License::factory(),
|
||||
'source_code_link' => fake()->url(),
|
||||
'featured' => fake()->boolean(),
|
||||
|
@ -8,6 +8,9 @@ use App\Models\SptVersion;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Factory<ModVersion>
|
||||
*/
|
||||
class ModVersionFactory extends Factory
|
||||
{
|
||||
protected $model = ModVersion::class;
|
||||
|
@ -6,6 +6,9 @@ use App\Models\SptVersion;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Factory<SptVersion>
|
||||
*/
|
||||
class SptVersionFactory extends Factory
|
||||
{
|
||||
protected $model = SptVersion::class;
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Random\RandomException;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
@ -14,6 +18,8 @@ class UserFactory extends Factory
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
protected $model = User::class;
|
||||
|
||||
/**
|
||||
* Define the user's default state.
|
||||
*
|
||||
|
@ -6,6 +6,9 @@ use App\Models\UserRole;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
* @extends Factory<UserRole>
|
||||
*/
|
||||
class UserRoleFactory extends Factory
|
||||
{
|
||||
protected $model = UserRole::class;
|
||||
|
@ -23,7 +23,7 @@ return new class extends PulseMigration
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
default => $table->string('key_hash'),
|
||||
};
|
||||
$table->mediumText('value');
|
||||
|
||||
@ -40,7 +40,7 @@ return new class extends PulseMigration
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
default => $table->string('key_hash'),
|
||||
};
|
||||
$table->bigInteger('value')->nullable();
|
||||
|
||||
@ -59,7 +59,7 @@ return new class extends PulseMigration
|
||||
match ($this->driver()) {
|
||||
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||
'sqlite' => $table->string('key_hash'),
|
||||
default => $table->string('key_hash'),
|
||||
};
|
||||
$table->string('aggregate');
|
||||
$table->decimal('value', 20, 2);
|
||||
|
@ -6,10 +6,11 @@ use App\Models\License;
|
||||
use App\Models\Mod;
|
||||
use App\Models\ModDependency;
|
||||
use App\Models\ModVersion;
|
||||
use App\Models\SptVersion;
|
||||
use App\Models\User;
|
||||
use App\Models\UserRole;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Laravel\Prompts\Progress;
|
||||
|
||||
use function Laravel\Prompts\progress;
|
||||
|
||||
@ -20,42 +21,44 @@ class DatabaseSeeder extends Seeder
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// how many of each "thing" to make during seeding
|
||||
$userCount = 100;
|
||||
$modCount = 300;
|
||||
$modVersionCount = 3000;
|
||||
// How many of each entity to create.
|
||||
$counts = [
|
||||
'license' => 10,
|
||||
'administrator' => 5,
|
||||
'moderator' => 5,
|
||||
'user' => 100,
|
||||
'mod' => 200,
|
||||
'modVersion' => 1500,
|
||||
];
|
||||
|
||||
// Create a few SPT versions.
|
||||
$spt_versions = SptVersion::factory(30)->create();
|
||||
// Licenses
|
||||
$licenses = License::factory($counts['license'])->create();
|
||||
|
||||
// Create some code licenses.
|
||||
$licenses = License::factory(10)->create();
|
||||
|
||||
// Add administrators.
|
||||
// Administrator Users
|
||||
$administratorRole = UserRole::factory()->administrator()->create();
|
||||
$testAccount = User::factory()->for($administratorRole, 'role')->create([
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
User::factory($counts['administrator'] - 1)->for($administratorRole, 'role')->create();
|
||||
|
||||
$this->command->outputComponents()->info("test account created: $testAccount->email");
|
||||
$this->command->outputComponents()->info("Test account created: {$testAccount->email}");
|
||||
|
||||
User::factory(4)->for($administratorRole, 'role')->create();
|
||||
|
||||
// Add moderators.
|
||||
// Moderator Users
|
||||
$moderatorRole = UserRole::factory()->moderator()->create();
|
||||
User::factory(5)->for($moderatorRole, 'role')->create();
|
||||
User::factory($counts['moderator'])->for($moderatorRole, 'role')->create();
|
||||
|
||||
// Add users
|
||||
// Users
|
||||
progress(
|
||||
label: 'adding users...',
|
||||
steps: $userCount,
|
||||
label: 'Adding Users...',
|
||||
steps: $counts['user'],
|
||||
callback: fn () => User::factory()->create()
|
||||
);
|
||||
|
||||
// get all users
|
||||
// All Users
|
||||
$allUsers = User::all();
|
||||
|
||||
// Add user follows
|
||||
/* We got a little ahead of ourselves here. This hasn't been merged yet!
|
||||
// User Follows
|
||||
progress(
|
||||
label: 'adding user follows ...',
|
||||
steps: $allUsers,
|
||||
@ -73,18 +76,20 @@ class DatabaseSeeder extends Seeder
|
||||
$user->following()->attach($following);
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
// Mods
|
||||
$mods = collect(progress(
|
||||
label: 'adding mods...',
|
||||
steps: $modCount,
|
||||
label: 'Adding Mods...',
|
||||
steps: $counts['mod'],
|
||||
callback: fn () => Mod::factory()->recycle([$licenses])->create()
|
||||
));
|
||||
|
||||
// attach users to mods
|
||||
// Attach users to mods
|
||||
progress(
|
||||
label: 'attaching mod users ...',
|
||||
label: 'Attaching users to mods...',
|
||||
steps: $mods,
|
||||
callback: function ($mod) use ($allUsers) {
|
||||
callback: function (Mod $mod, Progress $progress) use ($allUsers) {
|
||||
$userIds = $allUsers->random(rand(1, 3))->pluck('id')->toArray();
|
||||
$mod->users()->attach($userIds);
|
||||
}
|
||||
@ -92,26 +97,40 @@ class DatabaseSeeder extends Seeder
|
||||
|
||||
// Add mod versions, assigning them to the mods we just created.
|
||||
$modVersions = collect(progress(
|
||||
label: 'adding mods versions ...',
|
||||
steps: $modVersionCount,
|
||||
callback: fn () => ModVersion::factory()->recycle([$mods, $spt_versions])->create()
|
||||
label: 'Adding Mod Versions...',
|
||||
steps: $counts['modVersion'],
|
||||
callback: fn () => ModVersion::factory()->recycle([$mods])->create()
|
||||
));
|
||||
|
||||
// Add ModDependencies to a subset of ModVersions.
|
||||
// Add mod dependencies to *some* mod versions.
|
||||
progress(
|
||||
label: 'adding mods dependencies ...',
|
||||
label: 'Adding Mod Dependencies...',
|
||||
steps: $modVersions,
|
||||
callback: function ($modVersion) use ($mods) {
|
||||
$hasDependencies = rand(0, 100) < 30; // 30% chance to have dependencies
|
||||
if ($hasDependencies) {
|
||||
$dependencyMods = $mods->random(rand(1, 3)); // 1 to 3 dependencies
|
||||
foreach ($dependencyMods as $dependencyMod) {
|
||||
ModDependency::factory()->recycle([$modVersion, $dependencyMod])->create();
|
||||
}
|
||||
callback: function (ModVersion $modVersion, Progress $progress) use ($mods) {
|
||||
// 70% chance to not have dependencies
|
||||
if (rand(0, 9) >= 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose 1-3 random mods to be dependencies.
|
||||
$dependencyMods = $mods->random(rand(1, 3));
|
||||
foreach ($dependencyMods as $dependencyMod) {
|
||||
ModDependency::factory()->recycle([$modVersion, $dependencyMod])->create();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$this->command->outputComponents()->success('Database seeded');
|
||||
$this->command->outputComponents()->success('Initial seeding complete');
|
||||
|
||||
Artisan::call('app:search-sync');
|
||||
Artisan::call('app:resolve-versions');
|
||||
Artisan::call('app:count-mods');
|
||||
Artisan::call('app:update-downloads');
|
||||
$this->command->outputComponents()->warn('Jobs added to queue. Ensure Horizon is running!');
|
||||
|
||||
Artisan::call('cache:clear');
|
||||
$this->command->outputComponents()->info('Cache cleared');
|
||||
|
||||
$this->command->outputComponents()->success('Database seeding complete');
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user