Updates, Lots of Updates

No one reads these anyways. I updated a lot. There. Happy?
This commit is contained in:
Refringe 2024-05-31 17:44:52 -04:00
parent 45b9ebc096
commit c59615d7ac
Signed by: Refringe
GPG Key ID: 7715B85B4A6306ED
19 changed files with 400 additions and 89 deletions

View File

@ -223,6 +223,7 @@ class ImportWoltlabData extends Command
'source_code_link' => $this->fetchSourceLinkValue($modOptions),
'featured' => $mod->isFeatured,
'contains_ai_content' => $this->fetchContainsAiContentValue($modOptions),
'contains_ads' => $this->fetchContainsAdsValue($modOptions),
'disabled' => $mod->isDisabled,
'created_at' => Carbon::parse($mod->time, 'UTC'),
'updated_at' => Carbon::parse($mod->lastChangeTime, 'UTC'),
@ -305,6 +306,18 @@ class ImportWoltlabData extends Command
return false;
}
protected function fetchContainsAdsValue(array $options): bool
{
// Iterate over the options and find the 'optionID' of 3. That record will contain the Ad flag.
foreach ($options as $option) {
if ($option->optionID == 3) {
return (bool) $option->optionValue;
}
}
return false;
}
protected function fetchModThumbnail($command, &$curl, string $fileID, string $thumbnailHash, string $thumbnailExtension): string
{
if (empty($fileID) || empty($thumbnailHash) || empty($thumbnailExtension)) {
@ -400,6 +413,7 @@ class ImportWoltlabData extends Command
'spt_version_id' => SptVersion::whereHubId($versionLabel)->value('id'),
'virus_total_link' => $this->fetchVirusTotalLink($modOptions),
'downloads' => (int) $version->downloads,
'disabled' => (bool) $version->isDisabled,
'created_at' => Carbon::parse($version->uploadTime, 'UTC'),
'updated_at' => Carbon::parse($version->uploadTime, 'UTC'),
];
@ -424,14 +438,19 @@ class ImportWoltlabData extends Command
// in the 'optionValue' column. The 'optionID' of 6 should take precedence over 1. If neither are found, return
// an empty string.
foreach ($options as $option) {
if ($option->optionID == 5 && ! empty($option->optionValue)) {
if ($option->optionID == 6 && ! empty($option->optionValue)) {
return $option->optionValue;
}
if ($option->optionID == 1 && ! empty($option->optionValue)) {
if ($option->optionID == 2 && ! empty($option->optionValue)) {
return $option->optionValue;
}
}
return '';
}
protected function updateDisabledPropertty(): void
{
$this->output->newLine();
}
}

View File

@ -27,10 +27,11 @@ class ModController extends Controller
public function show(int $modId, string $slug)
{
$mod = Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
$mod = Mod::select()
->withLatestSptVersion()
->withTotalDownloads()
->with('user:id,name')
->with('license:id,name,link')
->find($modId);
if (! $mod || $mod->slug !== $slug) {

View File

@ -5,6 +5,7 @@ namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin \App\Models\Mod */
class ModResource extends JsonResource
{
public function toArray(Request $request): array

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Models\Scopes\DisabledScope;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -11,6 +12,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* @property string $slug
*/
class Mod extends Model
{
use HasFactory, SoftDeletes;
@ -24,6 +28,11 @@ class Mod extends Model
'source_code_link',
];
protected static function booted(): void
{
static::addGlobalScope(new DisabledScope);
}
protected function slug(): Attribute
{
return Attribute::make(
@ -49,7 +58,7 @@ class Mod extends Model
public function scopeWithTotalDownloads($query)
{
$query->addSelect(['total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads')
return $query->addSelect(['total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads')
->whereColumn('mod_id', 'mods.id'),
]);
}
@ -73,6 +82,7 @@ class Mod extends Model
->orderByDesc(DB::raw('naturalSort(version)'))
->take(1),
])
->havingNotNull('latest_spt_version_id')
->with(['latestSptVersion', 'latestSptVersion.sptVersion']);
}
@ -95,6 +105,7 @@ class Mod extends Model
->orderByDesc('updated_at')
->take(1)
)
->havingNotNull('last_updated_spt_version_id')
->with(['lastUpdatedVersion', 'lastUpdatedVersion.sptVersion']);
}
}

View File

@ -2,6 +2,7 @@
namespace App\Models;
use App\Models\Scopes\DisabledScope;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -20,6 +21,11 @@ class ModVersion extends Model
'downloads',
];
protected static function booted(): void
{
static::addGlobalScope(new DisabledScope);
}
public function mod(): BelongsTo
{
return $this->belongsTo(Mod::class);

View File

@ -0,0 +1,18 @@
<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class DisabledScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where($model->getTable().'.disabled', false);
}
}

View File

@ -10,7 +10,6 @@ abstract class Resource extends NovaResource
/**
* Build an "index" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
@ -22,7 +21,6 @@ abstract class Resource extends NovaResource
/**
* Build a Scout search query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Scout\Builder $query
* @return \Laravel\Scout\Builder
*/
@ -34,7 +32,6 @@ abstract class Resource extends NovaResource
/**
* Build a "detail" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
@ -48,7 +45,6 @@ abstract class Resource extends NovaResource
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/

View File

@ -12,7 +12,7 @@ class ModPolicy
*/
public function viewAny(User $user): bool
{
//
return false;
}
/**
@ -29,7 +29,7 @@ class ModPolicy
*/
public function create(User $user): bool
{
//
return false;
}
/**
@ -37,7 +37,7 @@ class ModPolicy
*/
public function update(User $user, Mod $mod): bool
{
//
return false;
}
/**
@ -45,7 +45,7 @@ class ModPolicy
*/
public function delete(User $user, Mod $mod): bool
{
//
return false;
}
/**
@ -53,7 +53,7 @@ class ModPolicy
*/
public function restore(User $user, Mod $mod): bool
{
//
return false;
}
/**
@ -61,6 +61,6 @@ class ModPolicy
*/
public function forceDelete(User $user, Mod $mod): bool
{
//
return false;
}
}

View File

@ -9,18 +9,18 @@
"license": "MIT",
"require": {
"php": "^8.3",
"ext-curl": "*",
"laravel/framework": "^11.0",
"laravel/jetstream": "^5.1",
"laravel/nova": "^4.0",
"laravel/pulse": "^1.1",
"laravel/sanctum": "^4.0",
"laravel/scout": "^10.9",
"laravel/tinker": "^2.9",
"livewire/livewire": "^3.0",
"ext-curl": "*"
"livewire/livewire": "^3.0"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.13",
"deployer/deployer": "^7.4",
"fakerphp/faker": "^1.23",
"larastan/larastan": "^2.9",
"laravel/pint": "^1.13",

123
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "40b3d2220c6f07a6c8182c49f5174d16",
"content-hash": "a7c13b1f59edf063a95ea214b1c73553",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -2170,6 +2170,84 @@
},
"time": "2024-04-10T19:39:58+00:00"
},
{
"name": "laravel/scout",
"version": "v10.9.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/scout.git",
"reference": "7bac13a61f1670b4314a65a13b8b12c6575270c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/scout/zipball/7bac13a61f1670b4314a65a13b8b12c6575270c8",
"reference": "7bac13a61f1670b4314a65a13b8b12c6575270c8",
"shasum": ""
},
"require": {
"illuminate/bus": "^9.0|^10.0|^11.0",
"illuminate/contracts": "^9.0|^10.0|^11.0",
"illuminate/database": "^9.0|^10.0|^11.0",
"illuminate/http": "^9.0|^10.0|^11.0",
"illuminate/pagination": "^9.0|^10.0|^11.0",
"illuminate/queue": "^9.0|^10.0|^11.0",
"illuminate/support": "^9.0|^10.0|^11.0",
"php": "^8.0",
"symfony/console": "^6.0|^7.0"
},
"require-dev": {
"algolia/algoliasearch-client-php": "^3.2",
"meilisearch/meilisearch-php": "^1.0",
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.31|^8.11|^9.0",
"php-http/guzzle7-adapter": "^1.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.3|^10.4",
"typesense/typesense-php": "^4.9.3"
},
"suggest": {
"algolia/algoliasearch-client-php": "Required to use the Algolia engine (^3.2).",
"meilisearch/meilisearch-php": "Required to use the Meilisearch engine (^1.0).",
"typesense/typesense-php": "Required to use the Typesense engine (^4.9)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "10.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Scout\\ScoutServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Scout\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Scout provides a driver based solution to searching your Eloquent models.",
"keywords": [
"algolia",
"laravel",
"search"
],
"support": {
"issues": "https://github.com/laravel/scout/issues",
"source": "https://github.com/laravel/scout"
},
"time": "2024-05-07T14:16:56+00:00"
},
{
"name": "laravel/serializable-closure",
"version": "v1.3.3",
@ -7236,49 +7314,6 @@
],
"time": "2024-04-12T11:20:37+00:00"
},
{
"name": "deployer/deployer",
"version": "v7.4.0",
"source": {
"type": "git",
"url": "https://github.com/deployphp/deployer.git",
"reference": "b438dc22545ab2ecc67d79c80c7a79c156de3599"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/deployphp/deployer/zipball/b438dc22545ab2ecc67d79c80c7a79c156de3599",
"reference": "b438dc22545ab2ecc67d79c80c7a79c156de3599",
"shasum": ""
},
"bin": [
"dep"
],
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Anton Medvedev",
"email": "anton@medv.io"
}
],
"description": "Deployment Tool",
"homepage": "https://deployer.org",
"support": {
"docs": "https://deployer.org/docs",
"issues": "https://github.com/deployphp/deployer/issues",
"source": "https://github.com/deployphp/deployer"
},
"funding": [
{
"url": "https://github.com/sponsors/antonmedv",
"type": "github"
}
],
"time": "2024-04-17T20:55:49+00:00"
},
{
"name": "fakerphp/faker",
"version": "v1.23.1",

202
config/scout.php Normal file
View File

@ -0,0 +1,202 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Search Engine
|--------------------------------------------------------------------------
|
| This option controls the default search connection that gets used while
| using Laravel Scout. This connection is used when syncing all models
| to the search service. You should adjust this based on your needs.
|
| Supported: "algolia", "meilisearch", "typesense",
| "database", "collection", "null"
|
*/
'driver' => env('SCOUT_DRIVER', 'algolia'),
/*
|--------------------------------------------------------------------------
| Index Prefix
|--------------------------------------------------------------------------
|
| Here you may specify a prefix that will be applied to all search index
| names used by Scout. This prefix may be useful if you have multiple
| "tenants" or applications sharing the same search infrastructure.
|
*/
'prefix' => env('SCOUT_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Queue Data Syncing
|--------------------------------------------------------------------------
|
| This option allows you to control if the operations that sync your data
| with your search engines are queued. When this is set to "true" then
| all automatic data syncing will get queued for better performance.
|
*/
'queue' => env('SCOUT_QUEUE', false),
/*
|--------------------------------------------------------------------------
| Database Transactions
|--------------------------------------------------------------------------
|
| This configuration option determines if your data will only be synced
| with your search indexes after every open database transaction has
| been committed, thus preventing any discarded data from syncing.
|
*/
'after_commit' => false,
/*
|--------------------------------------------------------------------------
| Chunk Sizes
|--------------------------------------------------------------------------
|
| These options allow you to control the maximum chunk size when you are
| mass importing data into the search engine. This allows you to fine
| tune each of these chunk sizes based on the power of the servers.
|
*/
'chunk' => [
'searchable' => 500,
'unsearchable' => 500,
],
/*
|--------------------------------------------------------------------------
| Soft Deletes
|--------------------------------------------------------------------------
|
| This option allows to control whether to keep soft deleted records in
| the search indexes. Maintaining soft deleted records can be useful
| if your application still needs to search for the records later.
|
*/
'soft_delete' => false,
/*
|--------------------------------------------------------------------------
| Identify User
|--------------------------------------------------------------------------
|
| This option allows you to control whether to notify the search engine
| of the user performing the search. This is sometimes useful if the
| engine supports any analytics based on this application's users.
|
| Supported engines: "algolia"
|
*/
'identify' => env('SCOUT_IDENTIFY', false),
/*
|--------------------------------------------------------------------------
| Algolia Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your Algolia settings. Algolia is a cloud hosted
| search engine which works great with Scout out of the box. Just plug
| in your application ID and admin API key to get started searching.
|
*/
'algolia' => [
'id' => env('ALGOLIA_APP_ID', ''),
'secret' => env('ALGOLIA_SECRET', ''),
],
/*
|--------------------------------------------------------------------------
| Meilisearch Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your Meilisearch settings. Meilisearch is an open
| source search engine with minimal configuration. Below, you can state
| the host and key information for your own Meilisearch installation.
|
| See: https://www.meilisearch.com/docs/learn/configuration/instance_options#all-instance-options
|
*/
'meilisearch' => [
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY'),
'index-settings' => [
// 'users' => [
// 'filterableAttributes'=> ['id', 'name', 'email'],
// ],
],
],
/*
|--------------------------------------------------------------------------
| Typesense Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your Typesense settings. Typesense is an open
| source search engine using minimal configuration. Below, you will
| state the host, key, and schema configuration for the instance.
|
*/
'typesense' => [
'client-settings' => [
'api_key' => env('TYPESENSE_API_KEY', 'xyz'),
'nodes' => [
[
'host' => env('TYPESENSE_HOST', 'localhost'),
'port' => env('TYPESENSE_PORT', '8108'),
'path' => env('TYPESENSE_PATH', ''),
'protocol' => env('TYPESENSE_PROTOCOL', 'http'),
],
],
'nearest_node' => [
'host' => env('TYPESENSE_HOST', 'localhost'),
'port' => env('TYPESENSE_PORT', '8108'),
'path' => env('TYPESENSE_PATH', ''),
'protocol' => env('TYPESENSE_PROTOCOL', 'http'),
],
'connection_timeout_seconds' => env('TYPESENSE_CONNECTION_TIMEOUT_SECONDS', 2),
'healthcheck_interval_seconds' => env('TYPESENSE_HEALTHCHECK_INTERVAL_SECONDS', 30),
'num_retries' => env('TYPESENSE_NUM_RETRIES', 3),
'retry_interval_seconds' => env('TYPESENSE_RETRY_INTERVAL_SECONDS', 1),
],
'model-settings' => [
// User::class => [
// 'collection-schema' => [
// 'fields' => [
// [
// 'name' => 'id',
// 'type' => 'string',
// ],
// [
// 'name' => 'name',
// 'type' => 'string',
// ],
// [
// 'name' => 'created_at',
// 'type' => 'int64',
// ],
// ],
// 'default_sorting_field' => 'created_at',
// ],
// 'search-parameters' => [
// 'query_by' => 'name'
// ],
// ],
],
],
];

View File

@ -26,6 +26,8 @@ class ModFactory extends Factory
'source_code_link' => $this->faker->url(),
'featured' => $this->faker->boolean,
'contains_ai_content' => $this->faker->boolean,
'contains_ads' => $this->faker->boolean,
'disabled' => $this->faker->boolean,
'created_at' => now(),
'updated_at' => now(),
];

View File

@ -22,6 +22,7 @@ class ModVersionFactory extends Factory
'spt_version_id' => SptVersion::factory(),
'virus_total_link' => $this->faker->url(),
'downloads' => $this->faker->randomNumber(),
'disabled' => $this->faker->boolean,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];

View File

@ -14,7 +14,7 @@ class SptVersionFactory extends Factory
{
return [
'version' => $this->faker->numerify('1.#.#'),
'color_class' => $this->faker->randomElement(['green', 'yellow', 'red', 'gray']),
'color_class' => $this->faker->randomElement(['red', 'green', 'emerald', 'lime', 'yellow', 'grey']),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];

View File

@ -23,6 +23,7 @@ return new class extends Migration
$table->string('source_code_link');
$table->boolean('featured')->default(false);
$table->boolean('contains_ai_content')->default(false);
$table->boolean('contains_ads')->default(false);
$table->boolean('disabled')->default(false);
$table->softDeletes();
$table->timestamps();

View File

@ -20,6 +20,7 @@ return new class extends Migration
$table->foreignIdFor(SptVersion::class)->constrained('spt_versions');
$table->string('virus_total_link');
$table->unsignedBigInteger('downloads');
$table->boolean('disabled')->default(false);
$table->softDeletes();
$table->timestamps();
});

View File

@ -1,24 +0,0 @@
import:
- recipe/laravel.php
config:
application: 'Forge'
repository: 'https://github.com/SPT-AKI/forge.git'
keep_releases: 5
hosts:
forge.sp-tarkov.com:
remote_user: deployer
deploy_path: '~/Forge'
branch: 'main'
env:
environment: production
labels:
env: prod
tasks:
build:
- run: uptime
after:
deploy:failed: deploy:unlock

View File

@ -16,6 +16,9 @@
<div class="flex flex-col w-full justify-between p-5">
<div>
<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>
<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 }}
@ -24,7 +27,7 @@
<p class="text-sm italic text-slate-600 dark:text-gray-200">By {{ $mod->user->name }}</p>
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ $mod->teaser }}</p>
</div>
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}" />
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
</div>
</div>
</div>

View File

@ -36,9 +36,47 @@
<a href="{{ $mod->latestSptVersion->link }}" class="block">
<button type="button" class="w-full">{{ __('Download Latest Version') }}</button>
</a>
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ __('Details') }}</h2>
</div>
<div class="p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl">
<h2 class="text-2xl font-bold text-gray-900 dark:text-gray-100">{{ __('Details') }}</h2>
<ul role="list" class="divide-y divide-gray-200 dark:divide-gray-800 text-gray-900 dark:text-gray-100">
@if($mod->license)
<li class="px-4 py-4 sm:px-0">
<h3>{{ __('License') }}</h3>
<p class="truncate">
<a href="{{ $mod->license->link }}" title="{{ $mod->license->name }}" target="_blank">
{{ $mod->license->name }}
</a>
</p>
</li>
@endif
@if($mod->source_code_link)
<li class="px-4 py-4 sm:px-0">
<h3>{{ __('Source Code') }}</h3>
<p class="truncate">
<a href="{{ $mod->source_code_link }}" title="{{ $mod->source_code_link }}" target="_blank">
{{ $mod->source_code_link }}
</a>
</p>
</li>
@endif
<li class="px-4 py-4 sm:px-0">
<h3>{{ __('Latest VirusTotal Result') }}</h3>
<p class="truncate">
<a href="{{ $mod->latestSptVersion->virus_total_link }}" title="{{ $mod->latestSptVersion->virus_total_link }}" target="_blank">
{{ $mod->latestSptVersion->virus_total_link }}
</a>
</p>
</li>
<li class="px-4 py-4 sm:px-0">
<h3>{{ __('Includes advertising?') }}</h3>
</li>
<li class="px-4 py-4 sm:px-0">
<h3>{{ __('Includes AI generated content?') }}</h3>
</li>
</ul>
</div>
</div>
</div>
</x-app-layout>