Initial commit

This commit is contained in:
hackerESQ
2024-08-01 13:53:10 -05:00
commit 1984bff865
142 changed files with 17783 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\CreatesNewUsers;
use Laravel\Jetstream\Jetstream;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => $this->passwordRules(),
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['accepted', 'required'] : '',
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', Password::default(), 'confirmed'];
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, mixed> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'],
])->validateWithBag('updateProfileInformation');
if (isset($input['photo'])) {
$user->updateProfilePhoto($input['photo']);
}
if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Actions\Jetstream;
use App\Models\User;
use Laravel\Jetstream\Contracts\DeletesUsers;
class DeleteUser implements DeletesUsers
{
/**
* Delete the given user.
*/
public function delete(User $user): void
{
$user->deleteProfilePhoto();
$user->tokens->each->delete();
$user->delete();
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers;
use App\Models\Holding;
use App\Models\Portfolio;
use App\Models\DailyChange;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Http\Requests\PortfolioRequest;
use Asantibanez\LivewireCharts\Models\LineChartModel;
use Livewire\Livewire;
use Livewire\Volt\Volt;
class PortfolioController extends Controller
{
/**
* Display form to create specified resource.
*
* @param Portfolio $portfolio
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('livewire.portfolio.create');
}
/**
* Display the specified resource.
*
* @param Portfolio $portfolio
* @return \Illuminate\Http\Response
*/
public function show(Portfolio $portfolio)
{
// $this->authorize('view', $portfolio);
return view('livewire.portfolio.show', [
'portfolio' => $portfolio
]);
}
}
@@ -0,0 +1,48 @@
<?php
namespace App\Interfaces\MarketData;
use Illuminate\Support\Collection;
interface MarketDataInterface
{
/**
* Does this symbol actually exist?
*
* @param String $symbol
*
* @return Bool
*/
public function exists(String $symbol): Bool;
/**
* Get quote data
*
* @param String $symbol
*
* @return Collection
*/
public function quote(String $symbol): Collection;
/**
* Get dividend data
*
* @param String $symbol
* @param \DateTimeInterface $startDate
* @param \DateTimeInterface $endDate
*
* @return Collection
*/
public function dividends(String $symbol, \DateTimeInterface $startDate, \DateTimeInterface $endDate): Collection;
/**
* Get split data
*
* @param String $symbol
* @param \DateTimeInterface $startDate
* @param \DateTimeInterface $endDate
*
* @return Collection
*/
public function splits(String $symbol, \DateTimeInterface $startDate, \DateTimeInterface $endDate): Collection;
}
@@ -0,0 +1,64 @@
<?php
namespace App\Interfaces\MarketData;
use App\Models\Holding;
use Illuminate\Support\Collection;
use Scheb\YahooFinanceApi\ApiClientFactory;
class YahooMarketData implements MarketDataInterface
{
// api client
public $client;
public function __construct() {
// create yahoo finance client factory
$this->client = ApiClientFactory::createApiClient();
}
public function exists(String $symbol): Bool
{
return $this->quote($symbol)->isNotEmpty();
}
public function quote($symbol): Collection
{
$quote = $this->client->getQuote($symbol);
if (empty($quote)) return collect();
return collect([
'name' => $quote->getLongName() ?? $quote->getShortName(),
'symbol' => $quote->getSymbol(),
'market_value' => $quote->getRegularMarketPrice(),
'fifty_two_week_high' => $quote->getFiftyTwoWeekHigh(),
'fifty_two_week_low' => $quote->getFiftyTwoWeekLow(),
]);
}
public function dividends($symbol, $startDate, $endDate): Collection
{
return collect($this->client->getHistoricalDividendData($symbol, $startDate, $endDate))
->map(function($dividend) use ($symbol) {
return [
'symbol' => $symbol,
'date' => $dividend->getDate()->format('Y-m-d H:i:s'),
'dividend_amount' => $dividend->getDividends(),
];
});
}
public function splits($symbol, $startDate, $endDate): Collection
{
return collect($this->client->getHistoricalSplitData($symbol, $startDate, $endDate))
->map(function($split) use ($symbol) {
$split_amount = explode(':', $split->getStockSplits());
return [
'symbol' => $symbol,
'date' => $split->getDate()->format('Y-m-d H:i:s'),
'split_amount' => $split_amount[0] / $split_amount[1],
];
});
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Portfolio extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title',
'notes',
'wishlist',
];
/**
*
* @return void
*/
protected static function boot()
{
parent::boot();
static::saved(function ($model) {
self::syncUsers($model);
});
}
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [];
/**
* The relationships that should always be eagerly loaded.
*
* @var array
*/
protected $with = ['users'];
/**
* The attributes that should be appended.
*
* @var array
*/
protected $appends = ['owner_id'];
public function users()
{
return $this->belongsToMany(User::class)->withPivot('owner');
}
// public function holdings()
// {
// return $this->hasMany(Holding::class, 'portfolio_id', 'id');
// }
// public function transactions()
// {
// return $this->hasMany(Transaction::class);
// }
public function scopeMyPortfolios()
{
return $this->whereRelation('users', 'id', auth()->user()->id);
}
public function scopeWithoutWishlists()
{
return $this->where(['wishlist' => false]);
}
public function getOwnerIdAttribute()
{
return $this->users()->firstWhere('owner', 1)?->id;
}
public static function syncUsers(self $model) {
// make sure we don't remove owner access
$user_id[$model->owner_id ?? auth()->user()->id] = ['owner' => true];
// // add other users
// foreach(request()->users ?? [] as $id) {
// $user_id[$id] = ['owner' => false];
// };
// save
$model->users()->sync($user_id);
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function portfolios()
{
return $this->belongsToMany(Portfolio::class)->withPivot('owner');
}
// public function daily_changes()
// {
// return $this->hasMany(DailyChange::class);
// }
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\Providers;
use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());
return Limit::perMinute(5)->by($throttleKey);
});
RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\Providers;
use App\Actions\Jetstream\DeleteUser;
use Illuminate\Support\ServiceProvider;
use Laravel\Jetstream\Jetstream;
class JetstreamServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
$this->configurePermissions();
Jetstream::deleteUsersUsing(DeleteUser::class);
}
/**
* Configure the permissions that are available within the application.
*/
protected function configurePermissions(): void
{
Jetstream::defaultApiTokenPermissions(['read']);
Jetstream::permissions([
'create',
'read',
'update',
'delete',
]);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Livewire\Volt\Volt;
class VoltServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot(): void
{
Volt::mount([
config('livewire.view_path', resource_path('views/livewire')),
resource_path('views/pages'),
]);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Support;
use Illuminate\Http\Request;
class Spotlight
{
public function search(Request $request)
{
// Do your search logic here
// IMPORTANT: apply any security concern here
if (!auth()->user()) {
return collect();
}
return collect([
[
'name' => 'Mary', // Any string
'description' => 'Software Engineer', // Any string
'link' => '/users/1', // Any valid route
'avatar' => 'http://...' // Any image url
]
]);
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class AppLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.app');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class CommonLayout extends Component
{
public function __construct(
// Slots
public mixed $body = null,
) { }
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.common');
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class GuestLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.guest');
}
}