Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .cspell/people.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ roardom
sindre
sindresorhus
sorhus
vinnie
vinnie
obi-wana
10 changes: 10 additions & 0 deletions app/Http/Controllers/AuthenticatedImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use App\Models\Article;
use App\Models\Category;
use App\Models\Collectible;
use App\Models\Playlist;
use App\Models\Scopes\ApprovedScope;
use App\Models\Torrent;
Expand Down Expand Up @@ -106,4 +107,13 @@ public function userIcon(User $user): \Symfony\Component\HttpFoundation\BinaryFi

return response()->file($path, self::HEADERS);
}

public function collectibleIcon(Collectible $collectible): \Symfony\Component\HttpFoundation\BinaryFileResponse
{
$path = Storage::disk('collectible-icons')->path($collectible->icon);

abort_unless(file_exists($path), 404);

return response()->file($path, self::HEADERS);
}
}
70 changes: 70 additions & 0 deletions app/Http/Controllers/Collectible/CollectibleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Obi-Wana
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/

namespace App\Http\Controllers\Collectible;

use App\Http\Controllers\Controller;
use App\Models\CollectibleCategory;
use App\Models\Collectible;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class CollectibleController extends Controller
{
/**
* Display All Collectibles.
*/
public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return view('collectible.index', [
'collectibles' => CollectibleCategory::with('collectibles')->has('collectibles')->orderBy('position')->get(),
]);
}

/**
* Display A Collectible.
*/
public function show(Collectible $collectible): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
$user = User::withAvg('history as avg_seedtime', 'seedtime')
->findOrFail(auth()->user()->id);
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
$userSeedSize = $user->seedingTorrents()->sum('size');

$requirements = $collectible->requirements;

return view('collectible.show', [
'user' => $user,
'userAvgSeedtime' => $userAvgSeedtime,
'userSeedSize' => $userSeedSize,
'collectible' => $collectible,
'userOwns' => $collectible->items()->where('user_id', '=', $user->id)->exists(),
'transactions' => $collectible->transactions()->latest()->take(50)->get(),
'offers' => $collectible->offers()->whereNull('filled_at')->get(),
'userIsSelling' => $collectible->offers()->where('user_id', $user->id)->whereNull('filled_at')->exists(),
'userTransaction' => $collectible->transactions()->where('buyer_id', $user->id)->latest()->first(),
'availableCount' => $collectible->items()->whereNull('user_id')->count(),
'requirements' => $requirements,
'userMeetsAllRequirements' => (
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
),
]);
}
}
175 changes: 175 additions & 0 deletions app/Http/Controllers/Collectible/CollectibleOfferController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<?php

declare(strict_types=1);

/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Obi-Wana
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/

namespace App\Http\Controllers\Collectible;

use App\Http\Controllers\Controller;
use App\Http\Requests\StoreCollectibleOfferRequest;
use App\Models\Collectible;
use App\Models\CollectibleOffer;
use App\Models\CollectibleTransaction;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class CollectibleOfferController extends Controller
{
/**
* Create A Collectible Offer.
*
* User creates a new offer.
*/
public function create(Collectible $collectible): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return view('collectible.offer.create', [
'collectibleItem' => $collectible->items()->where('user_id', '=', auth()->user()->id)->first(),
]);
}

/**
* Store A New Collectible Offer.
*
* Store User creates a new offer.
*/
public function store(Collectible $collectible, StoreCollectibleOfferRequest $request): \Illuminate\Http\RedirectResponse
{
$request->validated('offer');

$user = auth()->user();

$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();
$userIsSelling = $collectible->offers()->where('user_id', $user->id)->whereNull('filled_at')->exists();

if (! $collectible->resell) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("Reselling is disabled for this collectible.");
}

if (! $userOwns) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You can not sell this item, you do not own it.");
}

if ($collectible->in_stock) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You can not sell this item, its still in stock at the regular market.");
}

if ($userIsSelling) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You can not sell this item, you are already selling it.");
}

if ($request->offer['price'] > $collectible->price * 1.4) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("The highest selling price for this item is 1.2 times its original price.");
}

$offer = CollectibleOffer::create([
'collectible_id' => $collectible->id,
'user_id' => $user->id,
'price' => $request->offer['price'],
]);

return to_route('collectibles.show', ['collectible' => $collectible])
->with('success', "Offer created.");
}

/**
* Store An Collectible Offer Accept.
*
* User accepts an existing offer.
*/
public function update(CollectibleOffer $collectibleOffer): \Illuminate\Http\RedirectResponse
{
$user = auth()->user();
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
$userSeedSize = $user->seedingTorrents()->sum('size');

$collectible = $collectibleOffer->collectible;
$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();

if ($user->id === $collectibleOffer->user_id) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You can not accept your own offer.");
}

if ($userOwns) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You already own this item.");
}

if ($collectibleOffer->price > $user->seedbonus) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("Not enough BON.");
}

$requirements = $collectible->requirements;
$userMeetsAllRequirements = (
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
);

if (! $userMeetsAllRequirements) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You do not meet all requirements to buy this item!");
}

// Get the offered item
$collectibleItem = $collectible->items()->where('user_id', '=', $collectibleOffer->seller->id)->first();

DB::transaction(static function () use ($collectible, $collectibleItem, $collectibleOffer, $user): void {
$transaction = CollectibleTransaction::create([
'collectible_id' => $collectible->id,
'seller_id' => $collectibleOffer->seller->id,
'buyer_id' => $user->id,
'price' => $collectibleOffer->price,
]);

$collectibleItem->update([
'user_id' => $user->id,
]);

$collectibleOffer->update([
'filled_at' => now(),
]);

User::whereKey($transaction->seller_id)->increment('seedbonus', (float) $transaction->price);
User::whereKey($transaction->buyer_id)->decrement('seedbonus', (float) $transaction->price);
});

return to_route('collectibles.show', ['collectible' => $collectible])
->with('success', "Offer accepted.");
}

/**
* Destroy An Collectible Offer.
*
* User deletes his existing offer.
*/
public function destroy(Request $request, CollectibleOffer $collectibleOffer): \Illuminate\Http\RedirectResponse
{
abort_unless($request->user()->is($collectibleOffer->seller) || $request->user()->group->is_modo, 403);

$collectibleOffer->delete();

return to_route('collectibles.show', ['collectible' => $collectibleOffer->collectible])
->with('success', 'Offer deleted.');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author Obi-Wana
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/

namespace App\Http\Controllers\Collectible;

use App\Http\Controllers\Controller;
use App\Models\Collectible;
use App\Models\CollectibleTransaction;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class CollectibleTransactionController extends Controller
{
/**
* Buy A Collectible From Store.
*
* User buys in stock collectible.
*/
public function create(Collectible $collectible): \Illuminate\Http\RedirectResponse
{
$user = auth()->user();
$userAvgSeedtime = DB::table('history')->where('user_id', '=', $user->id)->avg('seedtime');
$userSeedSize = $user->seedingTorrents()->sum('size');

$userOwns = $collectible->items()->where('user_id', '=', $user->id)->exists();

if ($userOwns) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You already own this item.");
}

if ($collectible->price > $user->seedbonus) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors('Not enough BON.');
}

$requirements = $collectible->requirements;
$userMeetsAllRequirements = (
($requirements->min_uploaded === null || $user->uploaded >= $requirements->min_uploaded)
&& ($requirements->min_seedsize === null || $userSeedSize >= $requirements->min_seedsize)
&& ($requirements->min_avg_seedtime === null || $userAvgSeedtime >= $requirements->min_avg_seedtime)
&& ($requirements->min_ratio === null || $user->ratio >= $requirements->min_ratio)
&& ($requirements->min_age === null || $user->created_at->diffInSeconds(now()) >= $requirements->min_age)
);

if (! $userMeetsAllRequirements) {
return to_route('collectibles.show', ['collectible' => $collectible])
->withErrors("You do not meet all requirements to buy this item!");
}

// Get the first available collectible item that is not owned by a user
$collectibleItem = $collectible->items()->whereNull('user_id')->first();

$transaction = CollectibleTransaction::create([
'collectible_id' => $collectible->id,
'seller_id' => User::SYSTEM_USER_ID,
'buyer_id' => $user->id,
'price' => $collectible->price,
]);

$collectibleItem->update([
'user_id' => $user->id,
]);

$user->decrement('seedbonus', $collectibleItem->collectible->price);

return to_route('collectibles.show', ['collectible' => $collectible])
->with('success', "Collectible bought.");
}
}
Loading