mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-13 04:30:41 -05:00
Merge branch 'develop'
This commit is contained in:
commit
cd37b12bad
13
.env.full
13
.env.full
@ -6,7 +6,7 @@ APP_KEY=
|
|||||||
|
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_TIMEZONE=UTC
|
APP_TIMEZONE=UTC
|
||||||
APP_URL=https://localhost
|
APP_URL=http://localhost
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
APP_FAKER_LOCALE=en_US
|
APP_FAKER_LOCALE=en_US
|
||||||
@ -43,8 +43,8 @@ DB_HUB_PASSWORD=
|
|||||||
DB_HUB_CHARSET=utf8mb4
|
DB_HUB_CHARSET=utf8mb4
|
||||||
DB_HUB_COLLATION=utf8mb4_0900_ai_ci
|
DB_HUB_COLLATION=utf8mb4_0900_ai_ci
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=redis
|
||||||
SESSION_CONNECTION=mysql
|
SESSION_CONNECTION=default
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
SESSION_ENCRYPT=false
|
SESSION_ENCRYPT=false
|
||||||
SESSION_PATH=/
|
SESSION_PATH=/
|
||||||
@ -53,7 +53,7 @@ SESSION_DOMAIN=null
|
|||||||
BROADCAST_CONNECTION=log
|
BROADCAST_CONNECTION=log
|
||||||
|
|
||||||
FILESYSTEM_DISK=local
|
FILESYSTEM_DISK=local
|
||||||
ASSET_URL=http://localhost/storage
|
ASSET_URL="${APP_URL}"
|
||||||
ASSET_URL_LIVEWIRE=/vendor/livewire/livewire.js
|
ASSET_URL_LIVEWIRE=/vendor/livewire/livewire.js
|
||||||
|
|
||||||
CACHE_STORE=redis
|
CACHE_STORE=redis
|
||||||
@ -64,6 +64,7 @@ REDIS_HOST=redis
|
|||||||
REDIS_PASSWORD=null
|
REDIS_PASSWORD=null
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_QUEUE=default
|
REDIS_QUEUE=default
|
||||||
|
REDIS_QUEUE_CONNECTION=queue
|
||||||
REDIS_CACHE_CONNECTION=cache
|
REDIS_CACHE_CONNECTION=cache
|
||||||
|
|
||||||
SCOUT_QUEUE=true
|
SCOUT_QUEUE=true
|
||||||
@ -81,7 +82,7 @@ MAIL_ENCRYPTION=null
|
|||||||
MAIL_FROM_ADDRESS="no-reply@sp-tarkov.com"
|
MAIL_FROM_ADDRESS="no-reply@sp-tarkov.com"
|
||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
OCTANE_SERVER=frankenphp
|
OCTANE_SERVER=swoole
|
||||||
OCTANE_HTTPS=true
|
OCTANE_HTTPS=false
|
||||||
|
|
||||||
SAIL_XDEBUG_MODE=develop,debug,coverage
|
SAIL_XDEBUG_MODE=develop,debug,coverage
|
||||||
|
@ -29,7 +29,7 @@ DB_CONNECTION=sqlite
|
|||||||
DB_DATABASE=/Users/USER/Sites/forge/database/database.sqlite
|
DB_DATABASE=/Users/USER/Sites/forge/database/database.sqlite
|
||||||
|
|
||||||
FILESYSTEM_DISK=local
|
FILESYSTEM_DISK=local
|
||||||
ASSET_URL="${APP_URL}/storage"
|
ASSET_URL="${APP_URL}"
|
||||||
ASSET_URL_LIVEWIRE=/vendor/livewire/livewire.js
|
ASSET_URL_LIVEWIRE=/vendor/livewire/livewire.js
|
||||||
|
|
||||||
SESSION_DRIVER=file
|
SESSION_DRIVER=file
|
||||||
|
11
.github/README.md
vendored
11
.github/README.md
vendored
@ -27,9 +27,9 @@ We use [Laravel Sail](https://laravel.com/docs/11.x/sail) to mirror the services
|
|||||||
|
|
||||||
| Service | Authentication | Access Via Host |
|
| Service | Authentication | Access Via Host |
|
||||||
|----------------------------------|----------------|-----------------------------|
|
|----------------------------------|----------------|-----------------------------|
|
||||||
| Laravel Filament Admin Panel | Via User Role | <https://localhost/admin> |
|
| Laravel Filament Admin Panel | Via User Role | <http://localhost/admin> |
|
||||||
| Redis Queue Management (Horizon) | Via User Role | <https://localhost/horizon> |
|
| Redis Queue Management (Horizon) | Via User Role | <http://localhost/horizon> |
|
||||||
| Website Status (Pulse) | Via User Role | <https://localhost/pulse> |
|
| Website Status (Pulse) | Via User Role | <http://localhost/pulse> |
|
||||||
| Meilisearch WebUI | Local Only | <http://localhost:7700> |
|
| Meilisearch WebUI | Local Only | <http://localhost:7700> |
|
||||||
| Mailpit WebUI | Local Only | <http://localhost:8025> |
|
| Mailpit WebUI | Local Only | <http://localhost:8025> |
|
||||||
|
|
||||||
@ -59,6 +59,11 @@ sail artisan migrate:fresh –seed
|
|||||||
sail artisan horizon
|
sail artisan horizon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# Sync the local database with the Meilisearch server (requires horizon to be running):
|
||||||
|
sail artisan app:search-sync
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
# Install NPM dependencies from within the container:
|
# Install NPM dependencies from within the container:
|
||||||
sail npm install
|
sail npm install
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -8,7 +8,8 @@
|
|||||||
/.phpunit.cache
|
/.phpunit.cache
|
||||||
/.vscode
|
/.vscode
|
||||||
/caddy
|
/caddy
|
||||||
/data/caddy
|
/config/psysh
|
||||||
|
/data
|
||||||
/node_modules
|
/node_modules
|
||||||
/public/build
|
/public/build
|
||||||
/public/hot
|
/public/hot
|
||||||
@ -22,4 +23,3 @@ Homestead.json
|
|||||||
Homestead.yaml
|
Homestead.yaml
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
config/psysh
|
|
||||||
|
23
app/Console/Commands/SearchSync.php
Normal file
23
app/Console/Commands/SearchSync.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
class SearchSync extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'app:search-sync';
|
||||||
|
|
||||||
|
protected $description = 'Syncs all search settings and indexes with the database data.';
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
Artisan::call('scout:delete-all-indexes');
|
||||||
|
Artisan::call('scout:sync-index-settings');
|
||||||
|
Artisan::call('scout:import', ['model' => '\App\Models\Mod']);
|
||||||
|
Artisan::call('scout:import', ['model' => '\App\Models\User']);
|
||||||
|
|
||||||
|
$this->info('The search synchronisation jobs have been added to the queue.');
|
||||||
|
}
|
||||||
|
}
|
@ -28,9 +28,8 @@ class ModController extends Controller
|
|||||||
public function show(int $modId, string $slug)
|
public function show(int $modId, string $slug)
|
||||||
{
|
{
|
||||||
$mod = Mod::select()
|
$mod = Mod::select()
|
||||||
->withLatestSptVersion()
|
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with(['latestSptVersion', 'users:id,name'])
|
||||||
->with('license:id,name,link')
|
->with('license:id,name,link')
|
||||||
->find($modId);
|
->find($modId);
|
||||||
|
|
||||||
|
@ -20,22 +20,27 @@ class ModResource extends JsonResource
|
|||||||
'attributes' => [
|
'attributes' => [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'slug' => $this->slug,
|
'slug' => $this->slug,
|
||||||
|
'teaser' => $this->teaser,
|
||||||
'description' => $this->when(
|
'description' => $this->when(
|
||||||
$request->routeIs('api.v0.mods.show'),
|
$request->routeIs('api.v0.mods.show'),
|
||||||
$this->description
|
$this->description
|
||||||
),
|
),
|
||||||
'source_code_link' => $this->source_code_link,
|
|
||||||
'user_id' => $this->user_id,
|
|
||||||
'license_id' => $this->license_id,
|
'license_id' => $this->license_id,
|
||||||
|
'source_code_link' => $this->source_code_link,
|
||||||
|
'featured' => $this->featured,
|
||||||
|
'contains_ai_content' => $this->contains_ai_content,
|
||||||
|
'contains_ads' => $this->contains_ads,
|
||||||
'created_at' => $this->created_at,
|
'created_at' => $this->created_at,
|
||||||
|
'updated_at' => $this->updated_at,
|
||||||
],
|
],
|
||||||
'relationships' => [
|
'relationships' => [
|
||||||
'user' => [
|
'users' => [
|
||||||
'data' => [
|
'data' => $this->users->map(fn ($user) => [
|
||||||
'type' => 'user',
|
'type' => 'user',
|
||||||
'id' => $this->user_id,
|
'id' => $user->id,
|
||||||
],
|
])->toArray(),
|
||||||
// TODO: Provide 'links.self' to user profile:
|
|
||||||
|
// TODO: Provide 'links.self' to user profile
|
||||||
//'links' => ['self' => '#'],
|
//'links' => ['self' => '#'],
|
||||||
],
|
],
|
||||||
'license' => [
|
'license' => [
|
||||||
@ -45,11 +50,11 @@ class ModResource extends JsonResource
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'included' => [
|
'included' => $this->users->map(fn ($user) => new UserResource($user)),
|
||||||
new UserResource($this->user),
|
|
||||||
// TODO: Provide 'included' data for attached 'license':
|
// TODO: Provide 'included' data for attached 'license':
|
||||||
//new LicenseResource($this->license),
|
//new LicenseResource($this->license)
|
||||||
],
|
|
||||||
'links' => [
|
'links' => [
|
||||||
'self' => route('mod.show', [
|
'self' => route('mod.show', [
|
||||||
'mod' => $this->id,
|
'mod' => $this->id,
|
||||||
|
@ -16,7 +16,6 @@ class ModResource extends JsonResource
|
|||||||
'slug' => $this->slug,
|
'slug' => $this->slug,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'source_code_link' => $this->source_code_link,
|
'source_code_link' => $this->source_code_link,
|
||||||
'user_id' => $this->user_id,
|
|
||||||
'license_id' => $this->license_id,
|
'license_id' => $this->license_id,
|
||||||
'license' => new LicenseResource($this->whenLoaded('license')),
|
'license' => new LicenseResource($this->whenLoaded('license')),
|
||||||
'created_at' => $this->created_at,
|
'created_at' => $this->created_at,
|
||||||
|
@ -17,6 +17,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
|||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@ -35,6 +36,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
{
|
{
|
||||||
// Stream some data locally so that we don't have to keep accessing the Hub's database. Use MySQL temporary
|
// Stream some data locally so that we don't have to keep accessing the Hub's database. Use MySQL temporary
|
||||||
// tables to store the data to save on memory; we don't want this to be a hog.
|
// tables to store the data to save on memory; we don't want this to be a hog.
|
||||||
|
$this->bringFileAuthorsLocal();
|
||||||
$this->bringFileOptionsLocal();
|
$this->bringFileOptionsLocal();
|
||||||
$this->bringFileContentLocal();
|
$this->bringFileContentLocal();
|
||||||
$this->bringFileVersionLabelsLocal();
|
$this->bringFileVersionLabelsLocal();
|
||||||
@ -50,10 +52,32 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
// Ensure that we've disconnected from the Hub database, clearing temporary tables.
|
// Ensure that we've disconnected from the Hub database, clearing temporary tables.
|
||||||
DB::connection('mysql_hub')->disconnect();
|
DB::connection('mysql_hub')->disconnect();
|
||||||
|
|
||||||
// Reindex the Meilisearch index.
|
// Re-sync search.
|
||||||
Artisan::call('scout:delete-all-indexes');
|
Artisan::call('app:search-sync');
|
||||||
Artisan::call('scout:sync-index-settings');
|
}
|
||||||
Artisan::call('scout:import', ['model' => '\App\Models\Mod']);
|
|
||||||
|
/**
|
||||||
|
* Bring the file authors from the Hub database to the local database temporary table.
|
||||||
|
*/
|
||||||
|
protected function bringFileAuthorsLocal(): void
|
||||||
|
{
|
||||||
|
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_file_author');
|
||||||
|
DB::statement('CREATE TEMPORARY TABLE temp_file_author (
|
||||||
|
fileID INT,
|
||||||
|
userID INT
|
||||||
|
)');
|
||||||
|
|
||||||
|
DB::connection('mysql_hub')
|
||||||
|
->table('filebase1_file_author')
|
||||||
|
->orderBy('fileID')
|
||||||
|
->chunk(200, function ($relationships) {
|
||||||
|
foreach ($relationships as $relationship) {
|
||||||
|
DB::table('temp_file_author')->insert([
|
||||||
|
'fileID' => (int) $relationship->fileID,
|
||||||
|
'userID' => (int) $relationship->userID,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,6 +480,14 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
->chunkById(100, function (Collection $mods) use ($curl) {
|
->chunkById(100, function (Collection $mods) use ($curl) {
|
||||||
|
|
||||||
foreach ($mods as $mod) {
|
foreach ($mods as $mod) {
|
||||||
|
// Fetch any additional authors for the mod.
|
||||||
|
$modAuthors = DB::table('temp_file_author')
|
||||||
|
->where('fileID', $mod->fileID)
|
||||||
|
->pluck('userID')
|
||||||
|
->toArray();
|
||||||
|
$modAuthors[] = $mod->userID; // Add the primary author to the list.
|
||||||
|
$modAuthors = User::whereIn('hub_id', $modAuthors)->pluck('id')->toArray(); // Replace with local IDs.
|
||||||
|
|
||||||
$modContent = DB::table('temp_file_content')
|
$modContent = DB::table('temp_file_content')
|
||||||
->where('fileID', $mod->fileID)
|
->where('fileID', $mod->fileID)
|
||||||
->orderBy('fileID', 'desc')
|
->orderBy('fileID', 'desc')
|
||||||
@ -494,9 +526,9 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$insertData[] = [
|
$modData[] = [
|
||||||
'hub_id' => (int) $mod->fileID,
|
'hub_id' => (int) $mod->fileID,
|
||||||
'user_id' => User::whereHubId($mod->userID)->value('id'),
|
'users' => $modAuthors,
|
||||||
'name' => $modContent?->subject ?? '',
|
'name' => $modContent?->subject ?? '',
|
||||||
'slug' => Str::slug($modContent?->subject ?? ''),
|
'slug' => Str::slug($modContent?->subject ?? ''),
|
||||||
'teaser' => Str::limit($modContent?->teaser ?? ''),
|
'teaser' => Str::limit($modContent?->teaser ?? ''),
|
||||||
@ -504,18 +536,20 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
'thumbnail' => $this->fetchModThumbnail($curl, $mod->fileID, $mod->iconHash, $mod->iconExtension),
|
'thumbnail' => $this->fetchModThumbnail($curl, $mod->fileID, $mod->iconHash, $mod->iconExtension),
|
||||||
'license_id' => License::whereHubId($mod->licenseID)->value('id'),
|
'license_id' => License::whereHubId($mod->licenseID)->value('id'),
|
||||||
'source_code_link' => $optionSourceCode?->source_code_link ?? '',
|
'source_code_link' => $optionSourceCode?->source_code_link ?? '',
|
||||||
'featured' => (bool) $mod?->isFeatured,
|
'featured' => (bool) $mod->isFeatured,
|
||||||
'contains_ai_content' => (bool) $optionContainsAi?->contains_ai,
|
'contains_ai_content' => (bool) $optionContainsAi?->contains_ai,
|
||||||
'contains_ads' => (bool) $optionContainsAds?->contains_ads,
|
'contains_ads' => (bool) $optionContainsAds?->contains_ads,
|
||||||
'disabled' => (bool) $mod?->isDisabled,
|
'disabled' => (bool) $mod->isDisabled,
|
||||||
'created_at' => Carbon::parse($mod->time, 'UTC'),
|
'created_at' => Carbon::parse($mod->time, 'UTC'),
|
||||||
'updated_at' => Carbon::parse($mod->lastChangeTime, 'UTC'),
|
'updated_at' => Carbon::parse($mod->lastChangeTime, 'UTC'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! empty($insertData)) {
|
if (! empty($modData)) {
|
||||||
Mod::upsert($insertData, ['hub_id'], [
|
// Remove the user_id from the mod data before upserting.
|
||||||
'user_id',
|
$insertModData = array_map(fn ($mod) => Arr::except($mod, 'users'), $modData);
|
||||||
|
|
||||||
|
Mod::upsert($insertModData, ['hub_id'], [
|
||||||
'name',
|
'name',
|
||||||
'slug',
|
'slug',
|
||||||
'teaser',
|
'teaser',
|
||||||
@ -525,10 +559,15 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
'source_code_link',
|
'source_code_link',
|
||||||
'featured',
|
'featured',
|
||||||
'contains_ai_content',
|
'contains_ai_content',
|
||||||
|
'contains_ads',
|
||||||
'disabled',
|
'disabled',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at',
|
'updated_at',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
foreach ($modData as $mod) {
|
||||||
|
Mod::whereHubId($mod['hub_id'])->first()?->users()->sync($mod['users']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 'fileID');
|
}, 'fileID');
|
||||||
|
|
||||||
@ -666,6 +705,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
|||||||
public function failed(Exception $exception): void
|
public function failed(Exception $exception): void
|
||||||
{
|
{
|
||||||
// Explicitly drop the temporary tables.
|
// Explicitly drop the temporary tables.
|
||||||
|
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_author');
|
||||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
|
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
|
||||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content');
|
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content');
|
||||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_labels');
|
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_labels');
|
||||||
|
@ -42,8 +42,8 @@ class GlobalSearch extends Component
|
|||||||
|
|
||||||
if (Str::length($query)) {
|
if (Str::length($query)) {
|
||||||
$results['data'] = [
|
$results['data'] = [
|
||||||
'user' => User::search($query)->get(),
|
'user' => collect(User::search($query)->raw()['hits']),
|
||||||
'mod' => Mod::search($query)->get(),
|
'mod' => collect(Mod::search($query)->raw()['hits']),
|
||||||
];
|
];
|
||||||
$results['total'] = $this->countTotalResults($results['data']);
|
$results['total'] = $this->countTotalResults($results['data']);
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,6 @@ class License extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'link',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mods(): HasMany
|
public function mods(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Mod::class);
|
return $this->hasMany(Mod::class);
|
||||||
|
@ -7,7 +7,9 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -20,25 +22,18 @@ class Mod extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, Searchable, SoftDeletes;
|
use HasFactory, Searchable, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'user_id',
|
|
||||||
'name',
|
|
||||||
'slug',
|
|
||||||
'teaser',
|
|
||||||
'description',
|
|
||||||
'license_id',
|
|
||||||
'source_code_link',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
{
|
{
|
||||||
// Apply the global scope to exclude disabled mods.
|
// Apply the global scope to exclude disabled mods.
|
||||||
static::addGlobalScope(new DisabledScope);
|
static::addGlobalScope(new DisabledScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function user(): BelongsTo
|
/**
|
||||||
|
* The users that belong to the mod.
|
||||||
|
*/
|
||||||
|
public function users(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsToMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function license(): BelongsTo
|
public function license(): BelongsTo
|
||||||
@ -56,55 +51,15 @@ class Mod extends Model
|
|||||||
*/
|
*/
|
||||||
public function scopeWithTotalDownloads($query)
|
public function scopeWithTotalDownloads($query)
|
||||||
{
|
{
|
||||||
return $query->addSelect(['total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads')
|
return $query->addSelect([
|
||||||
->whereColumn('mod_id', 'mods.id'),
|
'total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads')
|
||||||
|
->whereColumn('mod_id', 'mods.id'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function latestSptVersion(): BelongsTo
|
public function lastUpdatedVersion(): HasOne
|
||||||
{
|
{
|
||||||
return $this->belongsTo(ModVersion::class, 'latest_spt_version_id');
|
return $this->hasOne(ModVersion::class)->orderByDesc('updated_at')->with('sptVersion');
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeWithLatestSptVersion($query)
|
|
||||||
{
|
|
||||||
return $query
|
|
||||||
->addSelect(['latest_spt_version_id' => ModVersion::select('id')
|
|
||||||
->whereColumn('mod_id', 'mods.id')
|
|
||||||
->orderByDesc(
|
|
||||||
SptVersion::select('version')
|
|
||||||
->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
|
|
||||||
->orderByDesc('version')
|
|
||||||
->take(1),
|
|
||||||
)
|
|
||||||
->orderByDesc('version')
|
|
||||||
->take(1),
|
|
||||||
])
|
|
||||||
->havingNotNull('latest_spt_version_id')
|
|
||||||
->with(['latestSptVersion', 'latestSptVersion.sptVersion']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function lastUpdatedVersion(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(ModVersion::class, 'last_updated_spt_version_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeWithLastUpdatedVersion($query)
|
|
||||||
{
|
|
||||||
return $query
|
|
||||||
->addSelect(['last_updated_spt_version_id' => ModVersion::select('id')
|
|
||||||
->whereColumn('mod_id', 'mods.id')
|
|
||||||
->orderByDesc('updated_at')
|
|
||||||
->take(1),
|
|
||||||
])
|
|
||||||
->orderByDesc(
|
|
||||||
ModVersion::select('updated_at')
|
|
||||||
->whereColumn('mod_id', 'mods.id')
|
|
||||||
->orderByDesc('updated_at')
|
|
||||||
->take(1)
|
|
||||||
)
|
|
||||||
->havingNotNull('last_updated_spt_version_id')
|
|
||||||
->with(['lastUpdatedVersion', 'lastUpdatedVersion.sptVersion']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,6 +67,8 @@ class Mod extends Model
|
|||||||
*/
|
*/
|
||||||
public function toSearchableArray(): array
|
public function toSearchableArray(): array
|
||||||
{
|
{
|
||||||
|
$latestSptVersion = $this->latestSptVersion()->first();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => (int) $this->id,
|
'id' => (int) $this->id,
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
@ -121,9 +78,25 @@ class Mod extends Model
|
|||||||
'featured' => $this->featured,
|
'featured' => $this->featured,
|
||||||
'created_at' => strtotime($this->created_at),
|
'created_at' => strtotime($this->created_at),
|
||||||
'updated_at' => strtotime($this->updated_at),
|
'updated_at' => strtotime($this->updated_at),
|
||||||
|
'latestSptVersion' => $latestSptVersion?->sptVersion->version,
|
||||||
|
'latestSptVersionColorClass' => $latestSptVersion?->sptVersion->color_class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function latestSptVersion(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(ModVersion::class)
|
||||||
|
->orderByDesc(
|
||||||
|
SptVersion::select('version')
|
||||||
|
->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
|
||||||
|
->orderByDesc('version')
|
||||||
|
->take(1),
|
||||||
|
)
|
||||||
|
->with('sptVersion')
|
||||||
|
->orderByDesc('version')
|
||||||
|
->take(1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the model should be searchable.
|
* Determine if the model should be searchable.
|
||||||
*/
|
*/
|
||||||
@ -155,6 +128,16 @@ class Mod extends Model
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'featured' => 'boolean',
|
||||||
|
'contains_ai_content' => 'boolean',
|
||||||
|
'contains_ads' => 'boolean',
|
||||||
|
'disabled' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure the slug is always lower case when retrieved and slugified when saved.
|
* Ensure the slug is always lower case when retrieved and slugified when saved.
|
||||||
*/
|
*/
|
||||||
|
@ -12,15 +12,6 @@ class ModVersion extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'mod_id',
|
|
||||||
'version',
|
|
||||||
'description',
|
|
||||||
'spt_version_id',
|
|
||||||
'virus_total_link',
|
|
||||||
'downloads',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
{
|
{
|
||||||
static::addGlobalScope(new DisabledScope);
|
static::addGlobalScope(new DisabledScope);
|
||||||
|
@ -11,11 +11,6 @@ class SptVersion extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'version',
|
|
||||||
'color_class',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function modVersions(): HasMany
|
public function modVersions(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(ModVersion::class);
|
return $this->hasMany(ModVersion::class);
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Notifications\ResetPassword;
|
||||||
|
use App\Notifications\VerifyEmail;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -25,12 +27,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
use Searchable;
|
use Searchable;
|
||||||
use TwoFactorAuthenticatable;
|
use TwoFactorAuthenticatable;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'email',
|
|
||||||
'password',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'password',
|
||||||
'remember_token',
|
'remember_token',
|
||||||
@ -42,9 +38,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
'profile_photo_url',
|
'profile_photo_url',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mods(): HasMany
|
public function mods(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Mod::class);
|
return $this->belongsToMany(Mod::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toSearchableArray(): array
|
public function toSearchableArray(): array
|
||||||
@ -82,6 +78,22 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
return Str::lower($this->role?->name) === 'administrator';
|
return Str::lower($this->role?->name) === 'administrator';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwritten to instead use the queued version of the VerifyEmail notification.
|
||||||
|
*/
|
||||||
|
public function sendEmailVerificationNotification(): void
|
||||||
|
{
|
||||||
|
$this->notify(new VerifyEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwritten to instead use the queued version of the ResetPassword notification.
|
||||||
|
*/
|
||||||
|
public function sendPasswordResetNotification(#[\SensitiveParameter] $token): void
|
||||||
|
{
|
||||||
|
$this->notify(new ResetPassword($token));
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -10,13 +10,6 @@ class UserRole extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'short_name',
|
|
||||||
'description',
|
|
||||||
'color_class',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function users(): HasMany
|
public function users(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(User::class);
|
return $this->hasMany(User::class);
|
||||||
|
25
app/Notifications/ResetPassword.php
Normal file
25
app/Notifications/ResetPassword.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Notifications\ResetPassword as OriginalResetPassword;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class exists solely to make the original notification queueable.
|
||||||
|
*/
|
||||||
|
class ResetPassword extends OriginalResetPassword implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct($token)
|
||||||
|
{
|
||||||
|
parent::__construct($token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(object $notifiable): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
20
app/Notifications/VerifyEmail.php
Normal file
20
app/Notifications/VerifyEmail.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Notifications\VerifyEmail as OriginalVerifyEmail;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class exists solely to make the original notification queueable.
|
||||||
|
*/
|
||||||
|
class VerifyEmail extends OriginalVerifyEmail implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function toArray(object $notifiable): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
@ -21,6 +22,9 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
// Allow mass assignment for all models. Be careful!
|
||||||
|
Model::unguard();
|
||||||
|
|
||||||
// This gate determines who can access the Pulse dashboard.
|
// This gate determines who can access the Pulse dashboard.
|
||||||
Gate::define('viewPulse', function (User $user) {
|
Gate::define('viewPulse', function (User $user) {
|
||||||
return $user->isAdmin();
|
return $user->isAdmin();
|
||||||
|
@ -26,7 +26,6 @@ class AdminPanelProvider extends PanelProvider
|
|||||||
->default()
|
->default()
|
||||||
->id('admin')
|
->id('admin')
|
||||||
->path('admin')
|
->path('admin')
|
||||||
->login()
|
|
||||||
->colors([
|
->colors([
|
||||||
'primary' => Color::Blue,
|
'primary' => Color::Blue,
|
||||||
])
|
])
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
namespace App\View\Components;
|
namespace App\View\Components;
|
||||||
|
|
||||||
use App\Models\Mod;
|
use App\Models\Mod;
|
||||||
|
use App\Models\ModVersion;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
class ModListSection extends Component
|
class ModListSection extends Component
|
||||||
@ -24,36 +26,51 @@ class ModListSection extends Component
|
|||||||
|
|
||||||
private function fetchFeaturedMods(): Collection
|
private function fetchFeaturedMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
return Cache::remember('homepage-featured-mods', now()->addMinutes(5), function () {
|
||||||
->withLatestSptVersion()
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with(['latestSptVersion', 'users:id,name'])
|
||||||
->where('featured', true)
|
->where('featured', true)
|
||||||
->latest()
|
->latest()
|
||||||
->limit(6)
|
->limit(6)
|
||||||
->get();
|
->get();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fetchLatestMods(): Collection
|
private function fetchLatestMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
|
return Cache::remember('homepage-latest-mods', now()->addMinutes(5), function () {
|
||||||
->withLatestSptVersion()
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with(['latestSptVersion', 'users:id,name'])
|
||||||
->latest()
|
->latest()
|
||||||
->limit(6)
|
->limit(6)
|
||||||
->get();
|
->get();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function fetchUpdatedMods(): Collection
|
private function fetchUpdatedMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
return Cache::remember('homepage-updated-mods', now()->addMinutes(5), function () {
|
||||||
->withLastUpdatedVersion()
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with(['lastUpdatedVersion', 'users:id,name'])
|
||||||
->latest()
|
->orderByDesc(
|
||||||
->limit(6)
|
ModVersion::select('updated_at')
|
||||||
->get();
|
->whereColumn('mod_id', 'mods.id')
|
||||||
|
->orderByDesc('updated_at')
|
||||||
|
->take(1)
|
||||||
|
)
|
||||||
|
->limit(6)
|
||||||
|
->get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('components.mod-list-section', [
|
||||||
|
'sections' => $this->getSections(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSections(): array
|
public function getSections(): array
|
||||||
@ -76,11 +93,4 @@ class ModListSection extends Component
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
|
||||||
{
|
|
||||||
return view('components.mod-list-section', [
|
|
||||||
'sections' => $this->getSections(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.3",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
|
"ext-intl": "*",
|
||||||
"aws/aws-sdk-php": "^3.314",
|
"aws/aws-sdk-php": "^3.314",
|
||||||
"filament/filament": "^3.2",
|
"filament/filament": "^3.2",
|
||||||
"http-interop/http-factory-guzzle": "^1.2",
|
"http-interop/http-factory-guzzle": "^1.2",
|
||||||
@ -33,7 +34,8 @@
|
|||||||
"laravel/sail": "^1.29",
|
"laravel/sail": "^1.29",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.1",
|
"nunomaduro/collision": "^8.1",
|
||||||
"phpunit/phpunit": "^11.2",
|
"pestphp/pest": "^2.34",
|
||||||
|
"pestphp/pest-plugin-stressless": "^2.2",
|
||||||
"spatie/laravel-ignition": "^2.8"
|
"spatie/laravel-ignition": "^2.8"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -50,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"phpstan": [
|
"phpstan": [
|
||||||
"./vendor/bin/phpstan analyse --debug --memory-limit=2G"
|
"./vendor/bin/phpstan analyse -c phpstan.neon --debug --memory-limit=2G"
|
||||||
],
|
],
|
||||||
"post-autoload-dump": [
|
"post-autoload-dump": [
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
|
1651
composer.lock
generated
1651
composer.lock
generated
File diff suppressed because it is too large
Load Diff
89
config/filament.php
Normal file
89
config/filament.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Broadcasting
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| By uncommenting the Laravel Echo configuration, you may connect Filament
|
||||||
|
| to any Pusher-compatible websockets server.
|
||||||
|
|
|
||||||
|
| This will allow your users to receive real-time notifications.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'broadcasting' => [
|
||||||
|
|
||||||
|
// 'echo' => [
|
||||||
|
// 'broadcaster' => 'pusher',
|
||||||
|
// 'key' => env('VITE_PUSHER_APP_KEY'),
|
||||||
|
// 'cluster' => env('VITE_PUSHER_APP_CLUSTER'),
|
||||||
|
// 'wsHost' => env('VITE_PUSHER_HOST'),
|
||||||
|
// 'wsPort' => env('VITE_PUSHER_PORT'),
|
||||||
|
// 'wssPort' => env('VITE_PUSHER_PORT'),
|
||||||
|
// 'authEndpoint' => '/broadcasting/auth',
|
||||||
|
// 'disableStats' => true,
|
||||||
|
// 'encrypted' => true,
|
||||||
|
// 'forceTLS' => true,
|
||||||
|
// ],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Filesystem Disk
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the storage disk Filament will use to store files. You may use
|
||||||
|
| any of the disks defined in the `config/filesystems.php`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Assets Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the directory where Filament's assets will be published to. It
|
||||||
|
| is relative to the `public` directory of your Laravel application.
|
||||||
|
|
|
||||||
|
| After changing the path, you should run `php artisan filament:assets`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'assets_path' => '/vendor/filament',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cache Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the directory that Filament will use to store cache files that
|
||||||
|
| are used to optimize the registration of components.
|
||||||
|
|
|
||||||
|
| After changing the path, you should run `php artisan filament:cache-components`.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'cache_path' => base_path('bootstrap/cache/filament'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Livewire Loading Delay
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This sets the delay before loading indicators appear.
|
||||||
|
|
|
||||||
|
| Setting this to 'none' makes indicators appear immediately, which can be
|
||||||
|
| desirable for high-latency connections. Setting it to 'default' applies
|
||||||
|
| Livewire's standard 200ms delay.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'livewire_loading_delay' => 'default',
|
||||||
|
|
||||||
|
];
|
@ -12,10 +12,13 @@ use Laravel\Octane\Events\WorkerErrorOccurred;
|
|||||||
use Laravel\Octane\Events\WorkerStarting;
|
use Laravel\Octane\Events\WorkerStarting;
|
||||||
use Laravel\Octane\Events\WorkerStopping;
|
use Laravel\Octane\Events\WorkerStopping;
|
||||||
use Laravel\Octane\Listeners\CloseMonologHandlers;
|
use Laravel\Octane\Listeners\CloseMonologHandlers;
|
||||||
|
use Laravel\Octane\Listeners\CollectGarbage;
|
||||||
|
use Laravel\Octane\Listeners\DisconnectFromDatabases;
|
||||||
use Laravel\Octane\Listeners\EnsureUploadedFilesAreValid;
|
use Laravel\Octane\Listeners\EnsureUploadedFilesAreValid;
|
||||||
use Laravel\Octane\Listeners\EnsureUploadedFilesCanBeMoved;
|
use Laravel\Octane\Listeners\EnsureUploadedFilesCanBeMoved;
|
||||||
use Laravel\Octane\Listeners\FlushOnce;
|
use Laravel\Octane\Listeners\FlushOnce;
|
||||||
use Laravel\Octane\Listeners\FlushTemporaryContainerInstances;
|
use Laravel\Octane\Listeners\FlushTemporaryContainerInstances;
|
||||||
|
use Laravel\Octane\Listeners\FlushUploadedFiles;
|
||||||
use Laravel\Octane\Listeners\ReportException;
|
use Laravel\Octane\Listeners\ReportException;
|
||||||
use Laravel\Octane\Listeners\StopWorkerIfNecessary;
|
use Laravel\Octane\Listeners\StopWorkerIfNecessary;
|
||||||
use Laravel\Octane\Octane;
|
use Laravel\Octane\Octane;
|
||||||
@ -35,7 +38,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'server' => env('OCTANE_SERVER', 'frankenphp'),
|
'server' => env('OCTANE_SERVER', 'swoole'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -48,7 +51,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'https' => env('OCTANE_HTTPS', true),
|
'https' => env('OCTANE_HTTPS', false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -132,7 +135,7 @@ return [
|
|||||||
],
|
],
|
||||||
|
|
||||||
'flush' => [
|
'flush' => [
|
||||||
//
|
\Barryvdh\Debugbar\LaravelDebugbar::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -18,7 +18,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'driver' => env('SESSION_DRIVER', 'database'),
|
'driver' => env('SESSION_DRIVER', 'redis'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -4,7 +4,6 @@ namespace Database\Factories;
|
|||||||
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
use App\Models\Mod;
|
use App\Models\Mod;
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Random\RandomException;
|
use Random\RandomException;
|
||||||
@ -18,10 +17,10 @@ class ModFactory extends Factory
|
|||||||
*/
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
|
|
||||||
$name = fake()->catchPhrase();
|
$name = fake()->catchPhrase();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'user_id' => User::factory(),
|
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'slug' => Str::slug($name),
|
'slug' => Str::slug($name),
|
||||||
'teaser' => fake()->sentence(),
|
'teaser' => fake()->sentence(),
|
||||||
|
@ -38,15 +38,6 @@ return new class extends Migration
|
|||||||
$table->string('token');
|
$table->string('token');
|
||||||
$table->timestamp('created_at')->nullable();
|
$table->timestamp('created_at')->nullable();
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('sessions', function (Blueprint $table) {
|
|
||||||
$table->string('id')->primary();
|
|
||||||
$table->foreignId('user_id')->nullable()->index();
|
|
||||||
$table->string('ip_address', 45)->nullable();
|
|
||||||
$table->text('user_agent')->nullable();
|
|
||||||
$table->longText('payload');
|
|
||||||
$table->integer('last_activity')->index();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +47,5 @@ return new class extends Migration
|
|||||||
{
|
{
|
||||||
Schema::dropIfExists('users');
|
Schema::dropIfExists('users');
|
||||||
Schema::dropIfExists('password_reset_tokens');
|
Schema::dropIfExists('password_reset_tokens');
|
||||||
Schema::dropIfExists('sessions');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
use App\Models\User;
|
|
||||||
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;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@ -16,10 +15,6 @@ return new class extends Migration
|
|||||||
->nullable()
|
->nullable()
|
||||||
->default(null)
|
->default(null)
|
||||||
->unique();
|
->unique();
|
||||||
$table->foreignIdFor(User::class)
|
|
||||||
->constrained('users')
|
|
||||||
->cascadeOnDelete()
|
|
||||||
->cascadeOnUpdate();
|
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('slug');
|
$table->string('slug');
|
||||||
$table->string('teaser');
|
$table->string('teaser');
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('mod_user', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('mod_id')->constrained()->cascadeOnUpdate()->cascadeOnDelete();
|
||||||
|
$table->foreignId('user_id')->constrained()->cascadeOnUpdate()->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['mod_id', 'user_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('mod_user');
|
||||||
|
}
|
||||||
|
};
|
@ -38,7 +38,12 @@ class DatabaseSeeder extends Seeder
|
|||||||
$users = User::factory(100)->create();
|
$users = User::factory(100)->create();
|
||||||
|
|
||||||
// Add 200 mods, assigning them to the users we just created.
|
// Add 200 mods, assigning them to the users we just created.
|
||||||
$mods = Mod::factory(200)->recycle([$users, $licenses])->create();
|
$allUsers = $users->merge([$administrator, $moderator]);
|
||||||
|
$mods = Mod::factory(200)->recycle([$licenses])->create();
|
||||||
|
foreach ($mods as $mod) {
|
||||||
|
$userIds = $allUsers->random(rand(1, 3))->pluck('id')->toArray();
|
||||||
|
$mod->users()->attach($userIds);
|
||||||
|
}
|
||||||
|
|
||||||
// Add 1000 mod versions, assigning them to the mods we just created.
|
// Add 1000 mod versions, assigning them to the mods we just created.
|
||||||
ModVersion::factory(1000)->recycle([$mods, $spt_versions])->create();
|
ModVersion::factory(1000)->recycle([$mods, $spt_versions])->create();
|
||||||
|
@ -19,9 +19,7 @@ services:
|
|||||||
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
|
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
|
||||||
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
|
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
|
||||||
IGNITION_LOCAL_SITES_PATH: '${PWD}'
|
IGNITION_LOCAL_SITES_PATH: '${PWD}'
|
||||||
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --host=localhost --port=443 --admin-port=2019 --https --watch"
|
SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=swoole --host=0.0.0.0 --port=80 --watch"
|
||||||
XDG_CONFIG_HOME: /var/www/html/config
|
|
||||||
XDG_DATA_HOME: /var/www/html/data
|
|
||||||
volumes:
|
volumes:
|
||||||
- '.:/var/www/html'
|
- '.:/var/www/html'
|
||||||
networks:
|
networks:
|
||||||
|
247
package-lock.json
generated
247
package-lock.json
generated
@ -489,9 +489,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.15",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -556,9 +556,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz",
|
||||||
"integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==",
|
"integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -570,9 +570,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz",
|
||||||
"integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==",
|
"integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -584,9 +584,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz",
|
||||||
"integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==",
|
"integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -598,9 +598,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz",
|
||||||
"integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==",
|
"integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -612,9 +612,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz",
|
||||||
"integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==",
|
"integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -626,9 +626,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz",
|
||||||
"integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==",
|
"integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -640,9 +640,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz",
|
||||||
"integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==",
|
"integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -654,9 +654,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz",
|
||||||
"integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==",
|
"integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -668,9 +668,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz",
|
||||||
"integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==",
|
"integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -682,9 +682,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz",
|
||||||
"integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==",
|
"integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -696,9 +696,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz",
|
||||||
"integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==",
|
"integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -710,9 +710,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz",
|
||||||
"integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==",
|
"integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -724,9 +724,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz",
|
||||||
"integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==",
|
"integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -738,9 +738,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz",
|
||||||
"integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==",
|
"integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -752,9 +752,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz",
|
||||||
"integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==",
|
"integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -766,9 +766,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz",
|
||||||
"integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==",
|
"integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -970,9 +970,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.23.1",
|
"version": "4.23.2",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz",
|
||||||
"integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==",
|
"integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -990,10 +990,10 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001629",
|
"caniuse-lite": "^1.0.30001640",
|
||||||
"electron-to-chromium": "^1.4.796",
|
"electron-to-chromium": "^1.4.820",
|
||||||
"node-releases": "^2.0.14",
|
"node-releases": "^2.0.14",
|
||||||
"update-browserslist-db": "^1.0.16"
|
"update-browserslist-db": "^1.1.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@ -1013,9 +1013,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001640",
|
"version": "1.0.30001642",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
|
||||||
"integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==",
|
"integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -1161,9 +1161,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.816",
|
"version": "1.4.829",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz",
|
||||||
"integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==",
|
"integrity": "sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@ -1365,9 +1365,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "10.4.2",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
"integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==",
|
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1381,9 +1381,6 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"glob": "dist/esm/bin.mjs"
|
"glob": "dist/esm/bin.mjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.18"
|
|
||||||
},
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
@ -1428,9 +1425,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.14.0",
|
"version": "2.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz",
|
||||||
"integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==",
|
"integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1494,17 +1491,14 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/jackspeak": {
|
"node_modules/jackspeak": {
|
||||||
"version": "3.4.0",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
"integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==",
|
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/cliui": "^8.0.2"
|
"@isaacs/cliui": "^8.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
},
|
},
|
||||||
@ -1523,9 +1517,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/laravel-vite-plugin": {
|
"node_modules/laravel-vite-plugin": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.0.5.tgz",
|
||||||
"integrity": "sha512-dEj8Q/Fsn0kKbOQ55bl/NmyJL+dD6OxnVaM/nNByw5XV4b00ky6FzXKVuHLDr4BvSJKH1y6oaOcEG5wKpCZ5+A==",
|
"integrity": "sha512-Zv+to82YLBknDCZ6g3iwOv9wZ7f6EWStb9pjSm7MGe9Mfoy5ynT2ssZbGsMr1udU6rDg9HOoYEVGw5Qf+p9zbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1581,14 +1575,11 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "10.3.0",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
"integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC"
|
||||||
"engines": {
|
|
||||||
"node": "14 || >=16.14"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
@ -1705,9 +1696,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.14",
|
"version": "2.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz",
|
||||||
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
|
"integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -1969,9 +1960,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-nested/node_modules/postcss-selector-parser": {
|
"node_modules/postcss-nested/node_modules/postcss-selector-parser": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
|
||||||
"integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==",
|
"integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2004,9 +1995,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
|
||||||
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
|
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -2175,9 +2166,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.18.0",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz",
|
||||||
"integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
|
"integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2191,22 +2182,22 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.18.0",
|
"@rollup/rollup-android-arm-eabi": "4.18.1",
|
||||||
"@rollup/rollup-android-arm64": "4.18.0",
|
"@rollup/rollup-android-arm64": "4.18.1",
|
||||||
"@rollup/rollup-darwin-arm64": "4.18.0",
|
"@rollup/rollup-darwin-arm64": "4.18.1",
|
||||||
"@rollup/rollup-darwin-x64": "4.18.0",
|
"@rollup/rollup-darwin-x64": "4.18.1",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.18.1",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.18.0",
|
"@rollup/rollup-linux-arm-musleabihf": "4.18.1",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.18.0",
|
"@rollup/rollup-linux-arm64-gnu": "4.18.1",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.18.0",
|
"@rollup/rollup-linux-arm64-musl": "4.18.1",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.18.1",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.18.0",
|
"@rollup/rollup-linux-riscv64-gnu": "4.18.1",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.18.0",
|
"@rollup/rollup-linux-s390x-gnu": "4.18.1",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.18.0",
|
"@rollup/rollup-linux-x64-gnu": "4.18.1",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.18.0",
|
"@rollup/rollup-linux-x64-musl": "4.18.1",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.18.0",
|
"@rollup/rollup-win32-arm64-msvc": "4.18.1",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.18.0",
|
"@rollup/rollup-win32-ia32-msvc": "4.18.1",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.18.0",
|
"@rollup/rollup-win32-x64-msvc": "4.18.1",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2427,9 +2418,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.4",
|
"version": "3.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz",
|
||||||
"integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==",
|
"integrity": "sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2478,9 +2469,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tailwindcss/node_modules/postcss-selector-parser": {
|
"node_modules/tailwindcss/node_modules/postcss-selector-parser": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
|
||||||
"integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==",
|
"integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2573,9 +2564,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz",
|
||||||
"integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==",
|
"integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2629,9 +2620,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-full-reload": {
|
"node_modules/vite-plugin-full-reload": {
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz",
|
||||||
"integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==",
|
"integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
includes:
|
includes:
|
||||||
- ./vendor/larastan/larastan/extension.neon
|
- ./vendor/larastan/larastan/extension.neon
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
paths:
|
paths:
|
||||||
- app/
|
- app/
|
||||||
|
excludePaths:
|
||||||
|
analyseAndScan:
|
||||||
|
- tests/
|
||||||
level: 4
|
level: 4
|
||||||
|
File diff suppressed because one or more lines are too long
116
public/vendor/livewire/livewire.esm.js
vendored
116
public/vendor/livewire/livewire.esm.js
vendored
@ -17,9 +17,9 @@ var __copyProps = (to, from, except, desc) => {
|
|||||||
};
|
};
|
||||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
||||||
|
|
||||||
// ../alpine/packages/alpinejs/dist/module.cjs.js
|
// node_modules/alpinejs/dist/module.cjs.js
|
||||||
var require_module_cjs = __commonJS({
|
var require_module_cjs = __commonJS({
|
||||||
"../alpine/packages/alpinejs/dist/module.cjs.js"(exports, module) {
|
"node_modules/alpinejs/dist/module.cjs.js"(exports, module) {
|
||||||
var __create2 = Object.create;
|
var __create2 = Object.create;
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
@ -3812,9 +3812,9 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/collapse/dist/module.cjs.js
|
// node_modules/@alpinejs/collapse/dist/module.cjs.js
|
||||||
var require_module_cjs2 = __commonJS({
|
var require_module_cjs2 = __commonJS({
|
||||||
"../alpine/packages/collapse/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/collapse/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -3933,9 +3933,9 @@ var require_module_cjs2 = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/focus/dist/module.cjs.js
|
// node_modules/@alpinejs/focus/dist/module.cjs.js
|
||||||
var require_module_cjs3 = __commonJS({
|
var require_module_cjs3 = __commonJS({
|
||||||
"../alpine/packages/focus/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/focus/dist/module.cjs.js"(exports, module) {
|
||||||
var __create2 = Object.create;
|
var __create2 = Object.create;
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
@ -4935,9 +4935,9 @@ var require_module_cjs3 = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/persist/dist/module.cjs.js
|
// node_modules/@alpinejs/persist/dist/module.cjs.js
|
||||||
var require_module_cjs4 = __commonJS({
|
var require_module_cjs4 = __commonJS({
|
||||||
"../alpine/packages/persist/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/persist/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -5024,9 +5024,9 @@ var require_module_cjs4 = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/intersect/dist/module.cjs.js
|
// node_modules/@alpinejs/intersect/dist/module.cjs.js
|
||||||
var require_module_cjs5 = __commonJS({
|
var require_module_cjs5 = __commonJS({
|
||||||
"../alpine/packages/intersect/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/intersect/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -5106,9 +5106,81 @@ var require_module_cjs5 = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/anchor/dist/module.cjs.js
|
// node_modules/@alpinejs/resize/dist/module.cjs.js
|
||||||
var require_module_cjs6 = __commonJS({
|
var require_module_cjs6 = __commonJS({
|
||||||
"../alpine/packages/anchor/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/resize/dist/module.cjs.js"(exports, module) {
|
||||||
|
var __defProp2 = Object.defineProperty;
|
||||||
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
|
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
||||||
|
var __export = (target, all2) => {
|
||||||
|
for (var name in all2)
|
||||||
|
__defProp2(target, name, { get: all2[name], enumerable: true });
|
||||||
|
};
|
||||||
|
var __copyProps2 = (to, from, except, desc) => {
|
||||||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||||||
|
for (let key of __getOwnPropNames2(from))
|
||||||
|
if (!__hasOwnProp2.call(to, key) && key !== except)
|
||||||
|
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
||||||
|
var module_exports = {};
|
||||||
|
__export(module_exports, {
|
||||||
|
default: () => module_default,
|
||||||
|
resize: () => src_default
|
||||||
|
});
|
||||||
|
module.exports = __toCommonJS(module_exports);
|
||||||
|
function src_default(Alpine19) {
|
||||||
|
Alpine19.directive("resize", Alpine19.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
|
||||||
|
let evaluator = evaluateLater(expression);
|
||||||
|
let evaluate = (width, height) => {
|
||||||
|
evaluator(() => {
|
||||||
|
}, { scope: { "$width": width, "$height": height } });
|
||||||
|
};
|
||||||
|
let off = modifiers.includes("document") ? onDocumentResize(evaluate) : onElResize(el, evaluate);
|
||||||
|
cleanup(() => off());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
function onElResize(el, callback) {
|
||||||
|
let observer = new ResizeObserver((entries) => {
|
||||||
|
let [width, height] = dimensions(entries);
|
||||||
|
callback(width, height);
|
||||||
|
});
|
||||||
|
observer.observe(el);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}
|
||||||
|
var documentResizeObserver;
|
||||||
|
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
|
||||||
|
function onDocumentResize(callback) {
|
||||||
|
documentResizeObserverCallbacks.add(callback);
|
||||||
|
if (!documentResizeObserver) {
|
||||||
|
documentResizeObserver = new ResizeObserver((entries) => {
|
||||||
|
let [width, height] = dimensions(entries);
|
||||||
|
documentResizeObserverCallbacks.forEach((i) => i(width, height));
|
||||||
|
});
|
||||||
|
documentResizeObserver.observe(document.documentElement);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
documentResizeObserverCallbacks.delete(callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function dimensions(entries) {
|
||||||
|
let width, height;
|
||||||
|
for (let entry of entries) {
|
||||||
|
width = entry.borderBoxSize[0].inlineSize;
|
||||||
|
height = entry.borderBoxSize[0].blockSize;
|
||||||
|
}
|
||||||
|
return [width, height];
|
||||||
|
}
|
||||||
|
var module_default = src_default;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// node_modules/@alpinejs/anchor/dist/module.cjs.js
|
||||||
|
var require_module_cjs7 = __commonJS({
|
||||||
|
"node_modules/@alpinejs/anchor/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -6644,9 +6716,9 @@ var require_nprogress = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/morph/dist/module.cjs.js
|
// node_modules/@alpinejs/morph/dist/module.cjs.js
|
||||||
var require_module_cjs7 = __commonJS({
|
var require_module_cjs8 = __commonJS({
|
||||||
"../alpine/packages/morph/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/morph/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -7006,9 +7078,9 @@ var require_module_cjs7 = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ../alpine/packages/mask/dist/module.cjs.js
|
// node_modules/@alpinejs/mask/dist/module.cjs.js
|
||||||
var require_module_cjs8 = __commonJS({
|
var require_module_cjs9 = __commonJS({
|
||||||
"../alpine/packages/mask/dist/module.cjs.js"(exports, module) {
|
"node_modules/@alpinejs/mask/dist/module.cjs.js"(exports, module) {
|
||||||
var __defProp2 = Object.defineProperty;
|
var __defProp2 = Object.defineProperty;
|
||||||
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
||||||
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
||||||
@ -8509,7 +8581,8 @@ var import_collapse = __toESM(require_module_cjs2());
|
|||||||
var import_focus = __toESM(require_module_cjs3());
|
var import_focus = __toESM(require_module_cjs3());
|
||||||
var import_persist2 = __toESM(require_module_cjs4());
|
var import_persist2 = __toESM(require_module_cjs4());
|
||||||
var import_intersect = __toESM(require_module_cjs5());
|
var import_intersect = __toESM(require_module_cjs5());
|
||||||
var import_anchor = __toESM(require_module_cjs6());
|
var import_resize = __toESM(require_module_cjs6());
|
||||||
|
var import_anchor = __toESM(require_module_cjs7());
|
||||||
|
|
||||||
// js/plugins/navigate/history.js
|
// js/plugins/navigate/history.js
|
||||||
var Snapshot = class {
|
var Snapshot = class {
|
||||||
@ -9445,8 +9518,8 @@ function fromQueryString(search) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// js/lifecycle.js
|
// js/lifecycle.js
|
||||||
var import_morph = __toESM(require_module_cjs7());
|
var import_morph = __toESM(require_module_cjs8());
|
||||||
var import_mask = __toESM(require_module_cjs8());
|
var import_mask = __toESM(require_module_cjs9());
|
||||||
var import_alpinejs5 = __toESM(require_module_cjs());
|
var import_alpinejs5 = __toESM(require_module_cjs());
|
||||||
function start() {
|
function start() {
|
||||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||||
@ -9455,6 +9528,7 @@ function start() {
|
|||||||
import_alpinejs5.default.plugin(import_morph.default);
|
import_alpinejs5.default.plugin(import_morph.default);
|
||||||
import_alpinejs5.default.plugin(history2);
|
import_alpinejs5.default.plugin(history2);
|
||||||
import_alpinejs5.default.plugin(import_intersect.default);
|
import_alpinejs5.default.plugin(import_intersect.default);
|
||||||
|
import_alpinejs5.default.plugin(import_resize.default);
|
||||||
import_alpinejs5.default.plugin(import_collapse.default);
|
import_alpinejs5.default.plugin(import_collapse.default);
|
||||||
import_alpinejs5.default.plugin(import_anchor.default);
|
import_alpinejs5.default.plugin(import_anchor.default);
|
||||||
import_alpinejs5.default.plugin(import_focus.default);
|
import_alpinejs5.default.plugin(import_focus.default);
|
||||||
|
80
public/vendor/livewire/livewire.js
vendored
80
public/vendor/livewire/livewire.js
vendored
@ -712,7 +712,7 @@
|
|||||||
uploadManager.cancelUpload(name, cancelledCallback);
|
uploadManager.cancelUpload(name, cancelledCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ../alpine/packages/alpinejs/dist/module.esm.js
|
// node_modules/alpinejs/dist/module.esm.js
|
||||||
var flushPending = false;
|
var flushPending = false;
|
||||||
var flushing = false;
|
var flushing = false;
|
||||||
var queue = [];
|
var queue = [];
|
||||||
@ -4762,7 +4762,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ../alpine/packages/collapse/dist/module.esm.js
|
// node_modules/@alpinejs/collapse/dist/module.esm.js
|
||||||
function src_default2(Alpine3) {
|
function src_default2(Alpine3) {
|
||||||
Alpine3.directive("collapse", collapse);
|
Alpine3.directive("collapse", collapse);
|
||||||
collapse.inline = (el, { modifiers }) => {
|
collapse.inline = (el, { modifiers }) => {
|
||||||
@ -4856,7 +4856,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
var module_default2 = src_default2;
|
var module_default2 = src_default2;
|
||||||
|
|
||||||
// ../alpine/packages/focus/dist/module.esm.js
|
// node_modules/@alpinejs/focus/dist/module.esm.js
|
||||||
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"];
|
var candidateSelectors = ["input", "select", "textarea", "a[href]", "button", "[tabindex]:not(slot)", "audio[controls]", "video[controls]", '[contenteditable]:not([contenteditable="false"])', "details>summary:first-of-type", "details"];
|
||||||
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
|
var candidateSelector = /* @__PURE__ */ candidateSelectors.join(",");
|
||||||
var NoElement = typeof Element === "undefined";
|
var NoElement = typeof Element === "undefined";
|
||||||
@ -5805,7 +5805,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
var module_default3 = src_default3;
|
var module_default3 = src_default3;
|
||||||
|
|
||||||
// ../alpine/packages/persist/dist/module.esm.js
|
// node_modules/@alpinejs/persist/dist/module.esm.js
|
||||||
function src_default4(Alpine3) {
|
function src_default4(Alpine3) {
|
||||||
let persist = () => {
|
let persist = () => {
|
||||||
let alias;
|
let alias;
|
||||||
@ -5867,7 +5867,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
var module_default4 = src_default4;
|
var module_default4 = src_default4;
|
||||||
|
|
||||||
// ../alpine/packages/intersect/dist/module.esm.js
|
// node_modules/@alpinejs/intersect/dist/module.esm.js
|
||||||
function src_default5(Alpine3) {
|
function src_default5(Alpine3) {
|
||||||
Alpine3.directive("intersect", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
Alpine3.directive("intersect", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||||
let evaluate3 = evaluateLater2(expression);
|
let evaluate3 = evaluateLater2(expression);
|
||||||
@ -5922,7 +5922,52 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
}
|
}
|
||||||
var module_default5 = src_default5;
|
var module_default5 = src_default5;
|
||||||
|
|
||||||
// ../alpine/packages/anchor/dist/module.esm.js
|
// node_modules/@alpinejs/resize/dist/module.esm.js
|
||||||
|
function src_default6(Alpine3) {
|
||||||
|
Alpine3.directive("resize", Alpine3.skipDuringClone((el, { value, expression, modifiers }, { evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||||
|
let evaluator = evaluateLater2(expression);
|
||||||
|
let evaluate3 = (width, height) => {
|
||||||
|
evaluator(() => {
|
||||||
|
}, { scope: { "$width": width, "$height": height } });
|
||||||
|
};
|
||||||
|
let off = modifiers.includes("document") ? onDocumentResize(evaluate3) : onElResize(el, evaluate3);
|
||||||
|
cleanup2(() => off());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
function onElResize(el, callback) {
|
||||||
|
let observer2 = new ResizeObserver((entries) => {
|
||||||
|
let [width, height] = dimensions(entries);
|
||||||
|
callback(width, height);
|
||||||
|
});
|
||||||
|
observer2.observe(el);
|
||||||
|
return () => observer2.disconnect();
|
||||||
|
}
|
||||||
|
var documentResizeObserver;
|
||||||
|
var documentResizeObserverCallbacks = /* @__PURE__ */ new Set();
|
||||||
|
function onDocumentResize(callback) {
|
||||||
|
documentResizeObserverCallbacks.add(callback);
|
||||||
|
if (!documentResizeObserver) {
|
||||||
|
documentResizeObserver = new ResizeObserver((entries) => {
|
||||||
|
let [width, height] = dimensions(entries);
|
||||||
|
documentResizeObserverCallbacks.forEach((i) => i(width, height));
|
||||||
|
});
|
||||||
|
documentResizeObserver.observe(document.documentElement);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
documentResizeObserverCallbacks.delete(callback);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function dimensions(entries) {
|
||||||
|
let width, height;
|
||||||
|
for (let entry of entries) {
|
||||||
|
width = entry.borderBoxSize[0].inlineSize;
|
||||||
|
height = entry.borderBoxSize[0].blockSize;
|
||||||
|
}
|
||||||
|
return [width, height];
|
||||||
|
}
|
||||||
|
var module_default6 = src_default6;
|
||||||
|
|
||||||
|
// node_modules/@alpinejs/anchor/dist/module.esm.js
|
||||||
var min = Math.min;
|
var min = Math.min;
|
||||||
var max = Math.max;
|
var max = Math.max;
|
||||||
var round = Math.round;
|
var round = Math.round;
|
||||||
@ -7096,7 +7141,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
platform: platformWithCache
|
platform: platformWithCache
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function src_default6(Alpine3) {
|
function src_default7(Alpine3) {
|
||||||
Alpine3.magic("anchor", (el) => {
|
Alpine3.magic("anchor", (el) => {
|
||||||
if (!el._x_anchor)
|
if (!el._x_anchor)
|
||||||
throw "Alpine: No x-anchor directive found on element using $anchor...";
|
throw "Alpine: No x-anchor directive found on element using $anchor...";
|
||||||
@ -7154,7 +7199,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
let unstyled = modifiers.includes("no-style");
|
let unstyled = modifiers.includes("no-style");
|
||||||
return { placement, offsetValue, unstyled };
|
return { placement, offsetValue, unstyled };
|
||||||
}
|
}
|
||||||
var module_default6 = src_default6;
|
var module_default7 = src_default7;
|
||||||
|
|
||||||
// js/plugins/navigate/history.js
|
// js/plugins/navigate/history.js
|
||||||
var Snapshot = class {
|
var Snapshot = class {
|
||||||
@ -8087,7 +8132,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
return data2;
|
return data2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ../alpine/packages/morph/dist/module.esm.js
|
// node_modules/@alpinejs/morph/dist/module.esm.js
|
||||||
function morph(from, toHtml, options) {
|
function morph(from, toHtml, options) {
|
||||||
monkeyPatchDomSetAttributeToAllowAtSymbols();
|
monkeyPatchDomSetAttributeToAllowAtSymbols();
|
||||||
let fromEl;
|
let fromEl;
|
||||||
@ -8417,13 +8462,13 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
to.setAttribute("id", fromId);
|
to.setAttribute("id", fromId);
|
||||||
to.id = fromId;
|
to.id = fromId;
|
||||||
}
|
}
|
||||||
function src_default7(Alpine3) {
|
function src_default8(Alpine3) {
|
||||||
Alpine3.morph = morph;
|
Alpine3.morph = morph;
|
||||||
}
|
}
|
||||||
var module_default7 = src_default7;
|
var module_default8 = src_default8;
|
||||||
|
|
||||||
// ../alpine/packages/mask/dist/module.esm.js
|
// node_modules/@alpinejs/mask/dist/module.esm.js
|
||||||
function src_default8(Alpine3) {
|
function src_default9(Alpine3) {
|
||||||
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
Alpine3.directive("mask", (el, { value, expression }, { effect: effect3, evaluateLater: evaluateLater2, cleanup: cleanup2 }) => {
|
||||||
let templateFn = () => expression;
|
let templateFn = () => expression;
|
||||||
let lastInputValue = "";
|
let lastInputValue = "";
|
||||||
@ -8585,22 +8630,23 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
|
|||||||
});
|
});
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
var module_default8 = src_default8;
|
var module_default9 = src_default9;
|
||||||
|
|
||||||
// js/lifecycle.js
|
// js/lifecycle.js
|
||||||
function start2() {
|
function start2() {
|
||||||
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
setTimeout(() => ensureLivewireScriptIsntMisplaced());
|
||||||
dispatch(document, "livewire:init");
|
dispatch(document, "livewire:init");
|
||||||
dispatch(document, "livewire:initializing");
|
dispatch(document, "livewire:initializing");
|
||||||
module_default.plugin(module_default7);
|
module_default.plugin(module_default8);
|
||||||
module_default.plugin(history2);
|
module_default.plugin(history2);
|
||||||
module_default.plugin(module_default5);
|
module_default.plugin(module_default5);
|
||||||
module_default.plugin(module_default2);
|
|
||||||
module_default.plugin(module_default6);
|
module_default.plugin(module_default6);
|
||||||
|
module_default.plugin(module_default2);
|
||||||
|
module_default.plugin(module_default7);
|
||||||
module_default.plugin(module_default3);
|
module_default.plugin(module_default3);
|
||||||
module_default.plugin(module_default4);
|
module_default.plugin(module_default4);
|
||||||
module_default.plugin(navigate_default);
|
module_default.plugin(navigate_default);
|
||||||
module_default.plugin(module_default8);
|
module_default.plugin(module_default9);
|
||||||
module_default.addRootSelector(() => "[wire\\:id]");
|
module_default.addRootSelector(() => "[wire\\:id]");
|
||||||
module_default.onAttributesAdded((el, attributes) => {
|
module_default.onAttributesAdded((el, attributes) => {
|
||||||
if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
|
if (!Array.from(attributes).some((attribute) => matchesForLivewireDirective(attribute.name)))
|
||||||
|
12
public/vendor/livewire/livewire.min.js
vendored
12
public/vendor/livewire/livewire.min.js
vendored
File diff suppressed because one or more lines are too long
6
public/vendor/livewire/livewire.min.js.map
vendored
6
public/vendor/livewire/livewire.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
public/vendor/livewire/manifest.json
vendored
2
public/vendor/livewire/manifest.json
vendored
@ -1,2 +1,2 @@
|
|||||||
|
|
||||||
{"/livewire.js":"c4fc8c5d"}
|
{"/livewire.js":"cc800bf4"}
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
||||||
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
||||||
{{ __('Before continuing, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
|
{{ __('Before continuing, please verify your email address. Click the button below and check your email for a verification link.') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (session('status') == 'verification-link-sent')
|
@if (session('status') == 'verification-link-sent')
|
||||||
<div class="mb-4 font-medium text-sm text-green-600 dark:text-green-400">
|
<div class="mb-4 font-medium text-sm text-green-600 dark:text-green-400">
|
||||||
{{ __('A new verification link has been sent to the email address you provided in your profile settings.') }}
|
{{ __('A verification link has been sent to the email address you provided in your profile settings.') }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<x-button type="submit">
|
<x-button type="submit">
|
||||||
{{ __('Resend Verification Email') }}
|
{{ __('Send Verification Email') }}
|
||||||
</x-button>
|
</x-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
<a href="/mod/{{ $result->id }}/{{ $result->slug }}" class="{{ $linkClass }}" role="listitem" tabindex="0">
|
<a href="/mod/{{ $result['id'] }}/{{ $result['slug'] }}" class="{{ $linkClass }}" role="listitem" tabindex="0" class="flex flex-col">
|
||||||
@if(empty($result->thumbnail))
|
@if(empty($result->thumbnail))
|
||||||
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="block dark:hidden h-6 w-6 flex-none border border-gray-200 group-hover/global-search-link:border-gray-400">
|
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result['name'] }}" alt="{{ $result['name'] }}" class="block dark:hidden h-6 w-6 self-center border border-gray-200 group-hover/global-search-link:border-gray-400">
|
||||||
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="hidden dark:block h-6 w-6 flex-none border border-gray-700 group-hover/global-search-link:border-gray-600">
|
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result['name'] }}" alt="{{ $result['name'] }}" class="hidden dark:block h-6 w-6 self-center border border-gray-700 group-hover/global-search-link:border-gray-600">
|
||||||
@else
|
@else
|
||||||
<img src="{{ Storage::url($result->thumbnail) }}" alt="{{ $result->name }}" class="h-6 w-6 flex-none">
|
<img src="{{ Storage::url($result['thumbnail']) }}" alt="{{ $result['name'] }}" class="h-6 w-6 self-center">
|
||||||
@endif
|
@endif
|
||||||
<p>{{ $result->name }}</p>
|
<p class="flex-grow">{{ $result['name'] }}</p>
|
||||||
|
<p class="ml-auto self-center badge-version {{ $result['latestSptVersionColorClass'] }} }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
|
||||||
|
{{ $result['latestSptVersion'] }}
|
||||||
|
</p>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<a href="#/{{ Str::slug($result->name) }}" class="{{ $linkClass }}">
|
<a href="#/{{ Str::slug($result['name']) }}" class="{{ $linkClass }}">
|
||||||
<p>{{ $result->name }}</p>
|
<p>{{ $result['name'] }}</p>
|
||||||
</a>
|
</a>
|
||||||
|
@ -10,13 +10,15 @@
|
|||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
||||||
</svg>
|
</svg>
|
||||||
</h4>
|
</h4>
|
||||||
@foreach($typeResults as $result)
|
<div class="divide-y divide-dashed divide-gray-200 dark:divide-gray-800">
|
||||||
@component('components.global-search-result-' . Str::lower($typeName), [
|
@foreach($typeResults as $result)
|
||||||
'result' => $result,
|
@component('components.global-search-result-' . Str::lower($typeName), [
|
||||||
'linkClass' => 'group/global-search-link flex flex-row gap-3 py-1.5 px-4 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors duration-100 ease-in-out',
|
'result' => $result,
|
||||||
])
|
'linkClass' => 'group/global-search-link flex flex-row gap-3 py-1.5 px-4 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors duration-200 ease-in-out',
|
||||||
@endcomponent
|
])
|
||||||
@endforeach
|
@endcomponent
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,15 +16,12 @@
|
|||||||
<div class="flex flex-col w-full justify-between p-5">
|
<div class="flex flex-col w-full justify-between p-5">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between items-center space-x-3">
|
<div class="flex justify-between items-center space-x-3">
|
||||||
@if(is_null($mod->{$versionScope}))
|
|
||||||
@dd($mod)
|
|
||||||
@endif
|
|
||||||
<h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3>
|
<h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3>
|
||||||
<span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
|
<span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
|
||||||
{{ $mod->{$versionScope}->sptVersion->version }}
|
{{ $mod->{$versionScope}->sptVersion->version }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm italic text-slate-600 dark:text-gray-200">By {{ $mod->user->name }}</p>
|
<p class="text-sm italic text-slate-600 dark:text-gray-200">By {{ $mod->users->pluck('name')->implode(', ') }}</p>
|
||||||
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ $mod->teaser }}</p>
|
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ $mod->teaser }}</p>
|
||||||
</div>
|
</div>
|
||||||
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
<div class="items-center justify-between gap-x-6 text-gray-200 bg-gray-900 dark:text-gray-900 dark:bg-gray-100 px-6 py-2.5 sm:pr-3.5 lg:pl-8">
|
<div class="items-center justify-between gap-x-6 text-gray-200 bg-gray-900 dark:text-gray-900 dark:bg-gray-100 px-6 py-2.5 sm:pr-3.5 lg:pl-8">
|
||||||
<p class="text-center text-sm leading-6">Notice: The Forge is currently under
|
<p class="text-center text-sm leading-6">Notice: The Forge is currently under <em class="font-bold">heavy</em> construction. Expect nothing to work. Data is reset every hour.</p>
|
||||||
<em class="font-bold">heavy</em> construction. Expect nothing to work.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
<video autoplay muted loop class="absolute inset-0 -z-10 h-full w-full object-cover z-0">
|
<video autoplay muted loop class="absolute inset-0 -z-10 h-full w-full object-cover z-0">
|
||||||
<source src="{{ Vite::asset('resources/video/welcome.mp4') }}" type="video/mp4">
|
<source src="{{ Vite::asset('resources/video/welcome.mp4') }}" type="video/mp4">
|
||||||
</video>
|
</video>
|
||||||
<div class="hidden sm:absolute sm:-top-10 sm:right-1/2 sm:-z-10 sm:mr-10 sm:block sm:transform-gpu sm:blur-3xl" aria-hidden="true">
|
<div class="hidden sm:absolute sm:-top-10 sm:right-1/2 sm:-z-11 sm:mr-10 sm:block sm:transform-gpu sm:blur-3xl" aria-hidden="true">
|
||||||
<div class="aspect-[1097/845] w-[68.5625rem] bg-gradient-to-tr from-[#333] to-[#000] opacity-20" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
<div class="aspect-[1097/845] w-[68.5625rem] bg-gradient-to-tr from-[#333] to-[#000] opacity-20" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute -top-52 left-1/2 -z-10 -translate-x-1/2 transform-gpu blur-3xl sm:top-[-28rem] sm:ml-16 sm:translate-x-0 sm:transform-gpu" aria-hidden="true">
|
<div class="absolute -top-52 left-1/2 -z-11 -translate-x-1/2 transform-gpu blur-3xl sm:top-[-28rem] sm:ml-16 sm:translate-x-0 sm:transform-gpu" aria-hidden="true">
|
||||||
<div class="aspect-[1097/845] w-[68.5625rem] bg-gradient-to-tr from-[#333] to-[#000] opacity-20" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
<div class="aspect-[1097/845] w-[68.5625rem] bg-gradient-to-tr from-[#333] to-[#000] opacity-20" style="clip-path: polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-auto max-w-2xl text-center">
|
<div class="mx-auto max-w-2xl text-center">
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
{{ $mod->latestSptVersion->version }}
|
{{ $mod->latestSptVersion->version }}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p>{{ __('Created by') }} {{ $mod->user->name }}</p>
|
<p>{{ __('Created by') }} {{ $mod->users->pluck('name')->implode(', ') }}</p>
|
||||||
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
||||||
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,6 +80,29 @@
|
|||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
@if (auth()->user()->isAdmin())
|
||||||
|
<div class="flex flex-col py-1.5">
|
||||||
|
<a href="/admin" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 21h16.5M4.5 3h15M5.25 3v18m13.5-18v18M9 6.75h1.5m-1.5 3h1.5m-1.5 3h1.5m3-6H15m-1.5 3H15m-1.5 3H15M9 21v-3.375c0-.621.504-1.125 1.125-1.125h3.75c.621 0 1.125.504 1.125 1.125V21" />
|
||||||
|
</svg>
|
||||||
|
{{ __('Admin Panel') }}
|
||||||
|
</a>
|
||||||
|
<a href="/pulse" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||||
|
</svg>
|
||||||
|
{{ __('Pulse Stats') }}
|
||||||
|
</a>
|
||||||
|
<a href="/horizon" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
|
||||||
|
</svg>
|
||||||
|
{{ __('Horizon Queue') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
<div class="flex flex-col py-1.5">
|
<div class="flex flex-col py-1.5">
|
||||||
<form method="POST" action="{{ route('logout') }}" x-data>
|
<form method="POST" action="{{ route('logout') }}" x-data>
|
||||||
@csrf
|
@csrf
|
||||||
|
@ -29,13 +29,15 @@
|
|||||||
<x-section-border />
|
<x-section-border />
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="mt-10 sm:mt-0">
|
@if (config('session.driver') === 'database')
|
||||||
@livewire('profile.logout-other-browser-sessions-form')
|
<div class="mt-10 sm:mt-0">
|
||||||
</div>
|
@livewire('profile.logout-other-browser-sessions-form')
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<x-section-border />
|
||||||
|
@endif
|
||||||
|
|
||||||
@if (Laravel\Jetstream\Jetstream::hasAccountDeletionFeatures())
|
@if (Laravel\Jetstream\Jetstream::hasAccountDeletionFeatures())
|
||||||
<x-section-border />
|
|
||||||
|
|
||||||
<div class="mt-10 sm:mt-0">
|
<div class="mt-10 sm:mt-0">
|
||||||
@livewire('profile.delete-user-form')
|
@livewire('profile.delete-user-form')
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Laravel\Jetstream\Features;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ApiTokenPermissionsTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_api_token_permissions_can_be_updated(): void
|
|
||||||
{
|
|
||||||
if (! Features::hasApiFeatures()) {
|
|
||||||
$this->markTestSkipped('API support is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$token = $user->tokens()->create([
|
|
||||||
'name' => 'Test Token',
|
|
||||||
'token' => Str::random(40),
|
|
||||||
'abilities' => ['create', 'read'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Livewire::test(ApiTokenManager::class)
|
|
||||||
->set(['managingPermissionsFor' => $token])
|
|
||||||
->set(['updateApiTokenForm' => [
|
|
||||||
'permissions' => [
|
|
||||||
'delete',
|
|
||||||
'missing-permission',
|
|
||||||
],
|
|
||||||
]])
|
|
||||||
->call('updateApiToken');
|
|
||||||
|
|
||||||
$this->assertTrue($user->fresh()->tokens->first()->can('delete'));
|
|
||||||
$this->assertFalse($user->fresh()->tokens->first()->can('read'));
|
|
||||||
$this->assertFalse($user->fresh()->tokens->first()->can('missing-permission'));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class AuthenticationTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_login_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/login');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_users_can_authenticate_using_the_login_screen(): void
|
|
||||||
{
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->post('/login', [
|
|
||||||
'email' => $user->email,
|
|
||||||
'password' => 'password',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
|
||||||
$response->assertRedirect(route('dashboard', absolute: false));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_users_can_not_authenticate_with_invalid_password(): void
|
|
||||||
{
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->post('/login', [
|
|
||||||
'email' => $user->email,
|
|
||||||
'password' => 'wrong-password',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertGuest();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class BrowserSessionsTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_other_browser_sessions_can_be_logged_out(): void
|
|
||||||
{
|
|
||||||
$this->actingAs(User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(LogoutOtherBrowserSessionsForm::class)
|
|
||||||
->set('password', 'password')
|
|
||||||
->call('logoutOtherBrowserSessions')
|
|
||||||
->assertSuccessful();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Jetstream\Features;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class CreateApiTokenTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_api_tokens_can_be_created(): void
|
|
||||||
{
|
|
||||||
if (! Features::hasApiFeatures()) {
|
|
||||||
$this->markTestSkipped('API support is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(ApiTokenManager::class)
|
|
||||||
->set(['createApiTokenForm' => [
|
|
||||||
'name' => 'Test Token',
|
|
||||||
'permissions' => [
|
|
||||||
'read',
|
|
||||||
'update',
|
|
||||||
],
|
|
||||||
]])
|
|
||||||
->call('createApiToken');
|
|
||||||
|
|
||||||
$this->assertCount(1, $user->fresh()->tokens);
|
|
||||||
$this->assertEquals('Test Token', $user->fresh()->tokens->first()->name);
|
|
||||||
$this->assertTrue($user->fresh()->tokens->first()->can('read'));
|
|
||||||
$this->assertFalse($user->fresh()->tokens->first()->can('delete'));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Jetstream\Features;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\DeleteUserForm;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class DeleteAccountTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_user_accounts_can_be_deleted(): void
|
|
||||||
{
|
|
||||||
if (! Features::hasAccountDeletionFeatures()) {
|
|
||||||
$this->markTestSkipped('Account deletion is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$component = Livewire::test(DeleteUserForm::class)
|
|
||||||
->set('password', 'password')
|
|
||||||
->call('deleteUser');
|
|
||||||
|
|
||||||
$this->assertNull($user->fresh());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_correct_password_must_be_provided_before_account_can_be_deleted(): void
|
|
||||||
{
|
|
||||||
if (! Features::hasAccountDeletionFeatures()) {
|
|
||||||
$this->markTestSkipped('Account deletion is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(DeleteUserForm::class)
|
|
||||||
->set('password', 'wrong-password')
|
|
||||||
->call('deleteUser')
|
|
||||||
->assertHasErrors(['password']);
|
|
||||||
|
|
||||||
$this->assertNotNull($user->fresh());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Laravel\Jetstream\Features;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class DeleteApiTokenTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_api_tokens_can_be_deleted(): void
|
|
||||||
{
|
|
||||||
if (! Features::hasApiFeatures()) {
|
|
||||||
$this->markTestSkipped('API support is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$token = $user->tokens()->create([
|
|
||||||
'name' => 'Test Token',
|
|
||||||
'token' => Str::random(40),
|
|
||||||
'abilities' => ['create', 'read'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Livewire::test(ApiTokenManager::class)
|
|
||||||
->set(['apiTokenIdBeingDeleted' => $token->id])
|
|
||||||
->call('deleteApiToken');
|
|
||||||
|
|
||||||
$this->assertCount(0, $user->fresh()->tokens);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Events\Verified;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Facades\Event;
|
|
||||||
use Illuminate\Support\Facades\URL;
|
|
||||||
use Laravel\Fortify\Features;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class EmailVerificationTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_email_verification_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::emailVerification())) {
|
|
||||||
$this->markTestSkipped('Email verification not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get('/email/verify');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_email_can_be_verified(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::emailVerification())) {
|
|
||||||
$this->markTestSkipped('Email verification not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
$verificationUrl = URL::temporarySignedRoute(
|
|
||||||
'verification.verify',
|
|
||||||
now()->addMinutes(60),
|
|
||||||
['id' => $user->id, 'hash' => sha1($user->email)]
|
|
||||||
);
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get($verificationUrl);
|
|
||||||
|
|
||||||
Event::assertDispatched(Verified::class);
|
|
||||||
|
|
||||||
$this->assertTrue($user->fresh()->hasVerifiedEmail());
|
|
||||||
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_email_can_not_verified_with_invalid_hash(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::emailVerification())) {
|
|
||||||
$this->markTestSkipped('Email verification not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = User::factory()->unverified()->create();
|
|
||||||
|
|
||||||
$verificationUrl = URL::temporarySignedRoute(
|
|
||||||
'verification.verify',
|
|
||||||
now()->addMinutes(60),
|
|
||||||
['id' => $user->id, 'hash' => sha1('wrong-email')]
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->actingAs($user)->get($verificationUrl);
|
|
||||||
|
|
||||||
$this->assertFalse($user->fresh()->hasVerifiedEmail());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
// use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A basic test example.
|
|
||||||
*/
|
|
||||||
public function test_the_application_returns_a_successful_response(): void
|
|
||||||
{
|
|
||||||
$response = $this->get('/');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class PasswordConfirmationTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_confirm_password_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->get('/user/confirm-password');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_password_can_be_confirmed(): void
|
|
||||||
{
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->post('/user/confirm-password', [
|
|
||||||
'password' => 'password',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response->assertRedirect();
|
|
||||||
$response->assertSessionHasNoErrors();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_password_is_not_confirmed_with_invalid_password(): void
|
|
||||||
{
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$response = $this->actingAs($user)->post('/user/confirm-password', [
|
|
||||||
'password' => 'wrong-password',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response->assertSessionHasErrors();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Notifications\ResetPassword;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Laravel\Fortify\Features;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class PasswordResetTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_reset_password_link_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::resetPasswords())) {
|
|
||||||
$this->markTestSkipped('Password updates are not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $this->get('/forgot-password');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_reset_password_link_can_be_requested(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::resetPasswords())) {
|
|
||||||
$this->markTestSkipped('Password updates are not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->post('/forgot-password', [
|
|
||||||
'email' => $user->email,
|
|
||||||
]);
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPassword::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_reset_password_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::resetPasswords())) {
|
|
||||||
$this->markTestSkipped('Password updates are not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->post('/forgot-password', [
|
|
||||||
'email' => $user->email,
|
|
||||||
]);
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPassword::class, function (object $notification) {
|
|
||||||
$response = $this->get('/reset-password/'.$notification->token);
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_password_can_be_reset_with_valid_token(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::resetPasswords())) {
|
|
||||||
$this->markTestSkipped('Password updates are not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
Notification::fake();
|
|
||||||
|
|
||||||
$user = User::factory()->create();
|
|
||||||
|
|
||||||
$this->post('/forgot-password', [
|
|
||||||
'email' => $user->email,
|
|
||||||
]);
|
|
||||||
|
|
||||||
Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) {
|
|
||||||
$response = $this->post('/reset-password', [
|
|
||||||
'token' => $notification->token,
|
|
||||||
'email' => $user->email,
|
|
||||||
'password' => 'password',
|
|
||||||
'password_confirmation' => 'password',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response->assertSessionHasNoErrors();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\UpdateProfileInformationForm;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class ProfileInformationTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_current_profile_information_is_available(): void
|
|
||||||
{
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$component = Livewire::test(UpdateProfileInformationForm::class);
|
|
||||||
|
|
||||||
$this->assertEquals($user->name, $component->state['name']);
|
|
||||||
$this->assertEquals($user->email, $component->state['email']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_profile_information_can_be_updated(): void
|
|
||||||
{
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(UpdateProfileInformationForm::class)
|
|
||||||
->set('state', ['name' => 'Test Name', 'email' => 'test@example.com'])
|
|
||||||
->call('updateProfileInformation');
|
|
||||||
|
|
||||||
$this->assertEquals('Test Name', $user->fresh()->name);
|
|
||||||
$this->assertEquals('test@example.com', $user->fresh()->email);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Fortify\Features;
|
|
||||||
use Laravel\Jetstream\Jetstream;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class RegistrationTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_registration_screen_can_be_rendered(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::registration())) {
|
|
||||||
$this->markTestSkipped('Registration support is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $this->get('/register');
|
|
||||||
|
|
||||||
$response->assertStatus(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_registration_screen_cannot_be_rendered_if_support_is_disabled(): void
|
|
||||||
{
|
|
||||||
if (Features::enabled(Features::registration())) {
|
|
||||||
$this->markTestSkipped('Registration support is enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $this->get('/register');
|
|
||||||
|
|
||||||
$response->assertStatus(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_new_users_can_register(): void
|
|
||||||
{
|
|
||||||
if (! Features::enabled(Features::registration())) {
|
|
||||||
$this->markTestSkipped('Registration support is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $this->post('/register', [
|
|
||||||
'name' => 'Test User',
|
|
||||||
'email' => 'test@example.com',
|
|
||||||
'password' => 'password',
|
|
||||||
'password_confirmation' => 'password',
|
|
||||||
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
|
||||||
$response->assertRedirect(route('dashboard', absolute: false));
|
|
||||||
}
|
|
||||||
}
|
|
9
tests/Feature/StressTest.php
Normal file
9
tests/Feature/StressTest.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use function Pest\Stressless\stress;
|
||||||
|
|
||||||
|
it('homepage has a fast response time', function () {
|
||||||
|
$result = stress('/');
|
||||||
|
|
||||||
|
expect($result->requests()->duration()->med())->toBeLessThan(100);
|
||||||
|
});
|
@ -1,76 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Laravel\Fortify\Features;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\TwoFactorAuthenticationForm;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class TwoFactorAuthenticationSettingsTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_two_factor_authentication_can_be_enabled(): void
|
|
||||||
{
|
|
||||||
if (! Features::canManageTwoFactorAuthentication()) {
|
|
||||||
$this->markTestSkipped('Two factor authentication is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$this->withSession(['auth.password_confirmed_at' => time()]);
|
|
||||||
|
|
||||||
Livewire::test(TwoFactorAuthenticationForm::class)
|
|
||||||
->call('enableTwoFactorAuthentication');
|
|
||||||
|
|
||||||
$user = $user->fresh();
|
|
||||||
|
|
||||||
$this->assertNotNull($user->two_factor_secret);
|
|
||||||
$this->assertCount(8, $user->recoveryCodes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_recovery_codes_can_be_regenerated(): void
|
|
||||||
{
|
|
||||||
if (! Features::canManageTwoFactorAuthentication()) {
|
|
||||||
$this->markTestSkipped('Two factor authentication is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$this->withSession(['auth.password_confirmed_at' => time()]);
|
|
||||||
|
|
||||||
$component = Livewire::test(TwoFactorAuthenticationForm::class)
|
|
||||||
->call('enableTwoFactorAuthentication')
|
|
||||||
->call('regenerateRecoveryCodes');
|
|
||||||
|
|
||||||
$user = $user->fresh();
|
|
||||||
|
|
||||||
$component->call('regenerateRecoveryCodes');
|
|
||||||
|
|
||||||
$this->assertCount(8, $user->recoveryCodes());
|
|
||||||
$this->assertCount(8, array_diff($user->recoveryCodes(), $user->fresh()->recoveryCodes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_two_factor_authentication_can_be_disabled(): void
|
|
||||||
{
|
|
||||||
if (! Features::canManageTwoFactorAuthentication()) {
|
|
||||||
$this->markTestSkipped('Two factor authentication is not enabled.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
$this->withSession(['auth.password_confirmed_at' => time()]);
|
|
||||||
|
|
||||||
$component = Livewire::test(TwoFactorAuthenticationForm::class)
|
|
||||||
->call('enableTwoFactorAuthentication');
|
|
||||||
|
|
||||||
$this->assertNotNull($user->fresh()->two_factor_secret);
|
|
||||||
|
|
||||||
$component->call('disableTwoFactorAuthentication');
|
|
||||||
|
|
||||||
$this->assertNull($user->fresh()->two_factor_secret);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Feature;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Laravel\Jetstream\Http\Livewire\UpdatePasswordForm;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
class UpdatePasswordTest extends TestCase
|
|
||||||
{
|
|
||||||
use RefreshDatabase;
|
|
||||||
|
|
||||||
public function test_password_can_be_updated(): void
|
|
||||||
{
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(UpdatePasswordForm::class)
|
|
||||||
->set('state', [
|
|
||||||
'current_password' => 'password',
|
|
||||||
'password' => 'new-password',
|
|
||||||
'password_confirmation' => 'new-password',
|
|
||||||
])
|
|
||||||
->call('updatePassword');
|
|
||||||
|
|
||||||
$this->assertTrue(Hash::check('new-password', $user->fresh()->password));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_current_password_must_be_correct(): void
|
|
||||||
{
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(UpdatePasswordForm::class)
|
|
||||||
->set('state', [
|
|
||||||
'current_password' => 'wrong-password',
|
|
||||||
'password' => 'new-password',
|
|
||||||
'password_confirmation' => 'new-password',
|
|
||||||
])
|
|
||||||
->call('updatePassword')
|
|
||||||
->assertHasErrors(['current_password']);
|
|
||||||
|
|
||||||
$this->assertTrue(Hash::check('password', $user->fresh()->password));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_new_passwords_must_match(): void
|
|
||||||
{
|
|
||||||
$this->actingAs($user = User::factory()->create());
|
|
||||||
|
|
||||||
Livewire::test(UpdatePasswordForm::class)
|
|
||||||
->set('state', [
|
|
||||||
'current_password' => 'password',
|
|
||||||
'password' => 'new-password',
|
|
||||||
'password_confirmation' => 'wrong-password',
|
|
||||||
])
|
|
||||||
->call('updatePassword')
|
|
||||||
->assertHasErrors(['password']);
|
|
||||||
|
|
||||||
$this->assertTrue(Hash::check('password', $user->fresh()->password));
|
|
||||||
}
|
|
||||||
}
|
|
38
tests/Feature/User/ApiTokenPermissionsTest.php
Executable file
38
tests/Feature/User/ApiTokenPermissionsTest.php
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Laravel\Jetstream\Features;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('api token permissions can be updated', function () {
|
||||||
|
if (Features::hasTeamFeatures()) {
|
||||||
|
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
|
||||||
|
} else {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $user->tokens()->create([
|
||||||
|
'name' => 'Test Token',
|
||||||
|
'token' => Str::random(40),
|
||||||
|
'abilities' => ['create', 'read'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(ApiTokenManager::class)
|
||||||
|
->set(['managingPermissionsFor' => $token])
|
||||||
|
->set(['updateApiTokenForm' => [
|
||||||
|
'permissions' => [
|
||||||
|
'delete',
|
||||||
|
'missing-permission',
|
||||||
|
],
|
||||||
|
]])
|
||||||
|
->call('updateApiToken');
|
||||||
|
|
||||||
|
expect($user->fresh()->tokens->first())
|
||||||
|
->can('delete')->toBeTrue()
|
||||||
|
->can('read')->toBeFalse()
|
||||||
|
->can('missing-permission')->toBeFalse();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::hasApiFeatures();
|
||||||
|
}, 'API support is not enabled.');
|
32
tests/Feature/User/AuthenticationTest.php
Executable file
32
tests/Feature/User/AuthenticationTest.php
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
test('login screen can be rendered', function () {
|
||||||
|
$response = $this->get('/login');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('users can authenticate using the login screen', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->post('/login', [
|
||||||
|
'email' => $user->email,
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertAuthenticated();
|
||||||
|
$response->assertRedirect(route('dashboard', absolute: false));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('users cannot authenticate with invalid password', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$this->post('/login', [
|
||||||
|
'email' => $user->email,
|
||||||
|
'password' => 'wrong-password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertGuest();
|
||||||
|
});
|
14
tests/Feature/User/BrowserSessionsTest.php
Executable file
14
tests/Feature/User/BrowserSessionsTest.php
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\LogoutOtherBrowserSessionsForm;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('other browser sessions can be logged out', function () {
|
||||||
|
$this->actingAs(User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(LogoutOtherBrowserSessionsForm::class)
|
||||||
|
->set('password', 'password')
|
||||||
|
->call('logoutOtherBrowserSessions')
|
||||||
|
->assertSuccessful();
|
||||||
|
});
|
32
tests/Feature/User/CreateApiTokenTest.php
Executable file
32
tests/Feature/User/CreateApiTokenTest.php
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Jetstream\Features;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('api tokens can be created', function () {
|
||||||
|
if (Features::hasTeamFeatures()) {
|
||||||
|
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
|
||||||
|
} else {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
}
|
||||||
|
|
||||||
|
Livewire::test(ApiTokenManager::class)
|
||||||
|
->set(['createApiTokenForm' => [
|
||||||
|
'name' => 'Test Token',
|
||||||
|
'permissions' => [
|
||||||
|
'read',
|
||||||
|
'update',
|
||||||
|
],
|
||||||
|
]])
|
||||||
|
->call('createApiToken');
|
||||||
|
|
||||||
|
expect($user->fresh()->tokens)->toHaveCount(1);
|
||||||
|
expect($user->fresh()->tokens->first())
|
||||||
|
->name->toEqual('Test Token')
|
||||||
|
->can('read')->toBeTrue()
|
||||||
|
->can('delete')->toBeFalse();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::hasApiFeatures();
|
||||||
|
}, 'API support is not enabled.');
|
31
tests/Feature/User/DeleteAccountTest.php
Executable file
31
tests/Feature/User/DeleteAccountTest.php
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Jetstream\Features;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\DeleteUserForm;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('user accounts can be deleted', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(DeleteUserForm::class)
|
||||||
|
->set('password', 'password')
|
||||||
|
->call('deleteUser');
|
||||||
|
|
||||||
|
expect($user->fresh())->toBeNull();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::hasAccountDeletionFeatures();
|
||||||
|
}, 'Account deletion is not enabled.');
|
||||||
|
|
||||||
|
test('correct password must be provided before account can be deleted', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(DeleteUserForm::class)
|
||||||
|
->set('password', 'wrong-password')
|
||||||
|
->call('deleteUser')
|
||||||
|
->assertHasErrors(['password']);
|
||||||
|
|
||||||
|
expect($user->fresh())->not->toBeNull();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::hasAccountDeletionFeatures();
|
||||||
|
}, 'Account deletion is not enabled.');
|
29
tests/Feature/User/DeleteApiTokenTest.php
Executable file
29
tests/Feature/User/DeleteApiTokenTest.php
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Laravel\Jetstream\Features;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\ApiTokenManager;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('api tokens can be deleted', function () {
|
||||||
|
if (Features::hasTeamFeatures()) {
|
||||||
|
$this->actingAs($user = User::factory()->withPersonalTeam()->create());
|
||||||
|
} else {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $user->tokens()->create([
|
||||||
|
'name' => 'Test Token',
|
||||||
|
'token' => Str::random(40),
|
||||||
|
'abilities' => ['create', 'read'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(ApiTokenManager::class)
|
||||||
|
->set(['apiTokenIdBeingDeleted' => $token->id])
|
||||||
|
->call('deleteApiToken');
|
||||||
|
|
||||||
|
expect($user->fresh()->tokens)->toHaveCount(0);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::hasApiFeatures();
|
||||||
|
}, 'API support is not enabled.');
|
60
tests/Feature/User/EmailVerificationTest.php
Executable file
60
tests/Feature/User/EmailVerificationTest.php
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\Events\Verified;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
|
use Laravel\Fortify\Features;
|
||||||
|
|
||||||
|
test('email verification screen can be rendered', function () {
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'email_verified_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->get('/email/verify');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::emailVerification());
|
||||||
|
}, 'Email verification not enabled.');
|
||||||
|
|
||||||
|
test('email can be verified', function () {
|
||||||
|
Event::fake();
|
||||||
|
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'email_verified_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$verificationUrl = URL::temporarySignedRoute(
|
||||||
|
'verification.verify',
|
||||||
|
now()->addMinutes(60),
|
||||||
|
['id' => $user->id, 'hash' => sha1($user->email)]
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->get($verificationUrl);
|
||||||
|
|
||||||
|
Event::assertDispatched(Verified::class);
|
||||||
|
|
||||||
|
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
||||||
|
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::emailVerification());
|
||||||
|
}, 'Email verification not enabled.');
|
||||||
|
|
||||||
|
test('email can not verified with invalid hash', function () {
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'email_verified_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$verificationUrl = URL::temporarySignedRoute(
|
||||||
|
'verification.verify',
|
||||||
|
now()->addMinutes(60),
|
||||||
|
['id' => $user->id, 'hash' => sha1('wrong-email')]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->actingAs($user)->get($verificationUrl);
|
||||||
|
|
||||||
|
expect($user->fresh()->hasVerifiedEmail())->toBeFalse();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::emailVerification());
|
||||||
|
}, 'Email verification not enabled.');
|
35
tests/Feature/User/PasswordConfirmationTest.php
Executable file
35
tests/Feature/User/PasswordConfirmationTest.php
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Jetstream\Features;
|
||||||
|
|
||||||
|
test('confirm password screen can be rendered', function () {
|
||||||
|
$user = Features::hasTeamFeatures()
|
||||||
|
? User::factory()->withPersonalTeam()->create()
|
||||||
|
: User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->get('/user/confirm-password');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('password can be confirmed', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->post('/user/confirm-password', [
|
||||||
|
'password' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertRedirect();
|
||||||
|
$response->assertSessionHasNoErrors();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('password is not confirmed with invalid password', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)->post('/user/confirm-password', [
|
||||||
|
'password' => 'wrong-password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertSessionHasErrors();
|
||||||
|
});
|
73
tests/Feature/User/PasswordResetTest.php
Executable file
73
tests/Feature/User/PasswordResetTest.php
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Notifications\ResetPassword;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Laravel\Fortify\Features;
|
||||||
|
|
||||||
|
test('reset password link screen can be rendered', function () {
|
||||||
|
$response = $this->get('/forgot-password');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::resetPasswords());
|
||||||
|
}, 'Password updates are not enabled.');
|
||||||
|
|
||||||
|
test('reset password link can be requested', function () {
|
||||||
|
Notification::fake();
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->post('/forgot-password', [
|
||||||
|
'email' => $user->email,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::assertSentTo($user, ResetPassword::class);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::resetPasswords());
|
||||||
|
}, 'Password updates are not enabled.');
|
||||||
|
|
||||||
|
test('reset password screen can be rendered', function () {
|
||||||
|
Notification::fake();
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->post('/forgot-password', [
|
||||||
|
'email' => $user->email,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::assertSentTo($user, ResetPassword::class, function (object $notification) {
|
||||||
|
$response = $this->get('/reset-password/'.$notification->token);
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::resetPasswords());
|
||||||
|
}, 'Password updates are not enabled.');
|
||||||
|
|
||||||
|
test('password can be reset with valid token', function () {
|
||||||
|
Notification::fake();
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->post('/forgot-password', [
|
||||||
|
'email' => $user->email,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) {
|
||||||
|
$response = $this->post('/reset-password', [
|
||||||
|
'token' => $notification->token,
|
||||||
|
'email' => $user->email,
|
||||||
|
'password' => 'password',
|
||||||
|
'password_confirmation' => 'password',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response->assertSessionHasNoErrors();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::resetPasswords());
|
||||||
|
}, 'Password updates are not enabled.');
|
26
tests/Feature/User/ProfileInformationTest.php
Executable file
26
tests/Feature/User/ProfileInformationTest.php
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\UpdateProfileInformationForm;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('current profile information is available', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
$component = Livewire::test(UpdateProfileInformationForm::class);
|
||||||
|
|
||||||
|
expect($component->state['name'])->toEqual($user->name);
|
||||||
|
expect($component->state['email'])->toEqual($user->email);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('profile information can be updated', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(UpdateProfileInformationForm::class)
|
||||||
|
->set('state', ['name' => 'Test Name', 'email' => 'test@example.com'])
|
||||||
|
->call('updateProfileInformation');
|
||||||
|
|
||||||
|
expect($user->fresh())
|
||||||
|
->name->toEqual('Test Name')
|
||||||
|
->email->toEqual('test@example.com');
|
||||||
|
});
|
35
tests/Feature/User/RegistrationTest.php
Executable file
35
tests/Feature/User/RegistrationTest.php
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Laravel\Fortify\Features;
|
||||||
|
use Laravel\Jetstream\Jetstream;
|
||||||
|
|
||||||
|
test('registration screen can be rendered', function () {
|
||||||
|
$response = $this->get('/register');
|
||||||
|
|
||||||
|
$response->assertStatus(200);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::registration());
|
||||||
|
}, 'Registration support is not enabled.');
|
||||||
|
|
||||||
|
test('registration screen cannot be rendered if support is disabled', function () {
|
||||||
|
$response = $this->get('/register');
|
||||||
|
|
||||||
|
$response->assertStatus(404);
|
||||||
|
})->skip(function () {
|
||||||
|
return Features::enabled(Features::registration());
|
||||||
|
}, 'Registration support is enabled.');
|
||||||
|
|
||||||
|
test('new users can register', function () {
|
||||||
|
$response = $this->post('/register', [
|
||||||
|
'name' => 'Test User',
|
||||||
|
'email' => 'test@example.com',
|
||||||
|
'password' => 'password',
|
||||||
|
'password_confirmation' => 'password',
|
||||||
|
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertAuthenticated();
|
||||||
|
$response->assertRedirect(route('dashboard', absolute: false));
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::enabled(Features::registration());
|
||||||
|
}, 'Registration support is not enabled.');
|
58
tests/Feature/User/TwoFactorAuthenticationSettingsTest.php
Executable file
58
tests/Feature/User/TwoFactorAuthenticationSettingsTest.php
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Laravel\Fortify\Features;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\TwoFactorAuthenticationForm;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('two factor authentication can be enabled', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create()->fresh());
|
||||||
|
|
||||||
|
$this->withSession(['auth.password_confirmed_at' => time()]);
|
||||||
|
|
||||||
|
Livewire::test(TwoFactorAuthenticationForm::class)
|
||||||
|
->call('enableTwoFactorAuthentication');
|
||||||
|
|
||||||
|
$user = $user->fresh();
|
||||||
|
|
||||||
|
expect($user->two_factor_secret)->not->toBeNull();
|
||||||
|
expect($user->recoveryCodes())->toHaveCount(8);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::canManageTwoFactorAuthentication();
|
||||||
|
}, 'Two factor authentication is not enabled.');
|
||||||
|
|
||||||
|
test('recovery codes can be regenerated', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create()->fresh());
|
||||||
|
|
||||||
|
$this->withSession(['auth.password_confirmed_at' => time()]);
|
||||||
|
|
||||||
|
$component = Livewire::test(TwoFactorAuthenticationForm::class)
|
||||||
|
->call('enableTwoFactorAuthentication')
|
||||||
|
->call('regenerateRecoveryCodes');
|
||||||
|
|
||||||
|
$user = $user->fresh();
|
||||||
|
|
||||||
|
$component->call('regenerateRecoveryCodes');
|
||||||
|
|
||||||
|
expect($user->recoveryCodes())->toHaveCount(8);
|
||||||
|
expect(array_diff($user->recoveryCodes(), $user->fresh()->recoveryCodes()))->toHaveCount(8);
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::canManageTwoFactorAuthentication();
|
||||||
|
}, 'Two factor authentication is not enabled.');
|
||||||
|
|
||||||
|
test('two factor authentication can be disabled', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create()->fresh());
|
||||||
|
|
||||||
|
$this->withSession(['auth.password_confirmed_at' => time()]);
|
||||||
|
|
||||||
|
$component = Livewire::test(TwoFactorAuthenticationForm::class)
|
||||||
|
->call('enableTwoFactorAuthentication');
|
||||||
|
|
||||||
|
$this->assertNotNull($user->fresh()->two_factor_secret);
|
||||||
|
|
||||||
|
$component->call('disableTwoFactorAuthentication');
|
||||||
|
|
||||||
|
expect($user->fresh()->two_factor_secret)->toBeNull();
|
||||||
|
})->skip(function () {
|
||||||
|
return ! Features::canManageTwoFactorAuthentication();
|
||||||
|
}, 'Two factor authentication is not enabled.');
|
50
tests/Feature/User/UpdatePasswordTest.php
Executable file
50
tests/Feature/User/UpdatePasswordTest.php
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Laravel\Jetstream\Http\Livewire\UpdatePasswordForm;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
test('password can be updated', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(UpdatePasswordForm::class)
|
||||||
|
->set('state', [
|
||||||
|
'current_password' => 'password',
|
||||||
|
'password' => 'new-password',
|
||||||
|
'password_confirmation' => 'new-password',
|
||||||
|
])
|
||||||
|
->call('updatePassword');
|
||||||
|
|
||||||
|
expect(Hash::check('new-password', $user->fresh()->password))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('current password must be correct', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(UpdatePasswordForm::class)
|
||||||
|
->set('state', [
|
||||||
|
'current_password' => 'wrong-password',
|
||||||
|
'password' => 'new-password',
|
||||||
|
'password_confirmation' => 'new-password',
|
||||||
|
])
|
||||||
|
->call('updatePassword')
|
||||||
|
->assertHasErrors(['current_password']);
|
||||||
|
|
||||||
|
expect(Hash::check('password', $user->fresh()->password))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('new passwords must match', function () {
|
||||||
|
$this->actingAs($user = User::factory()->create());
|
||||||
|
|
||||||
|
Livewire::test(UpdatePasswordForm::class)
|
||||||
|
->set('state', [
|
||||||
|
'current_password' => 'password',
|
||||||
|
'password' => 'new-password',
|
||||||
|
'password_confirmation' => 'wrong-password',
|
||||||
|
])
|
||||||
|
->call('updatePassword')
|
||||||
|
->assertHasErrors(['password']);
|
||||||
|
|
||||||
|
expect(Hash::check('password', $user->fresh()->password))->toBeTrue();
|
||||||
|
});
|
48
tests/Pest.php
Executable file
48
tests/Pest.php
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Test Case
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||||
|
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||||
|
| need to change it using the "uses()" function to bind a different classes or traits.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class)->in('Feature');
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Expectations
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||||
|
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||||
|
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
expect()->extend('toBeOne', function () {
|
||||||
|
return $this->toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Functions
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||||
|
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||||
|
| global functions to help you to reduce the number of lines of code in your test files.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
function something()
|
||||||
|
{
|
||||||
|
// ..
|
||||||
|
}
|
@ -1,16 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Tests\Unit;
|
test('that true is true', function () {
|
||||||
|
expect(true)->toBe(true);
|
||||||
use PHPUnit\Framework\TestCase;
|
});
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A basic test example.
|
|
||||||
*/
|
|
||||||
public function test_that_true_is_true(): void
|
|
||||||
{
|
|
||||||
$this->assertTrue(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user