Mohammad Akbari
4 years ago
116 changed files with 9796 additions and 1298 deletions
-
6.env.example
-
136app/Console/Commands/CostCommand.php
-
268app/Http/Controllers/AuthController.php
-
195app/Http/Controllers/BusinessController.php
-
69app/Http/Controllers/CreditController.php
-
203app/Http/Controllers/FileController.php
-
80app/Http/Controllers/InvoiceController.php
-
159app/Http/Controllers/ProjectController.php
-
47app/Http/Controllers/SprintController.php
-
37app/Http/Controllers/StatusController.php
-
40app/Http/Controllers/SystemController.php
-
37app/Http/Controllers/TagController.php
-
89app/Http/Controllers/TaskFileController.php
-
69app/Http/Controllers/UserController.php
-
71app/Http/Controllers/WorkflowController.php
-
34app/Http/Resources/BusinessResource.php
-
37app/Http/Resources/FileResource.php
-
34app/Http/Resources/FingerprintResource.php
-
38app/Http/Resources/ProjectResource.php
-
32app/Http/Resources/TransactionResource.php
-
34app/Http/Resources/UserResource.php
-
10app/Models/Activity.php
-
486app/Models/Business.php
-
24app/Models/Cost.php
-
75app/Models/File.php
-
30app/Models/Fingerprint.php
-
204app/Models/Project.php
-
29app/Models/SoftDeletes.php
-
73app/Models/Sprint.php
-
55app/Models/Status.php
-
69app/Models/System.php
-
58app/Models/Tag.php
-
25app/Models/Task.php
-
164app/Models/Transaction.php
-
207app/Models/User.php
-
88app/Models/Workflow.php
-
21app/Providers/AuthServiceProvider.php
-
40app/Rules/MaxBound.php
-
18app/Scopes/BusinessScope.php
-
15app/Utilities/Avatar/DefaultConversionFileNamer.php
-
41app/Utilities/Avatar/DefaultPathGenerator.php
-
43app/Utilities/BusinessInfoRequestMixin.php
-
83app/Utilities/Payload.php
-
51app/Utilities/RequestMixin.php
-
46app/Utilities/Zarinpal/Drivers/DriverInterface.php
-
197app/Utilities/Zarinpal/Drivers/RestDriver.php
-
24app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php
-
46app/Utilities/Zarinpal/Laravel/ZarinpalServiceProvider.php
-
130app/Utilities/Zarinpal/Zarinpal.php
-
15composer.json
-
4094composer.lock
-
232config.default/app.php
-
0config.default/auth.php
-
0config.default/broadcasting.php
-
0config.default/cache.php
-
34config.default/cors.php
-
0config.default/database.php
-
72config.default/filesystems.php
-
0config.default/hashing.php
-
104config.default/logging.php
-
0config.default/mail.php
-
0config.default/queue.php
-
33config.default/services.php
-
0config.default/session.php
-
0config.default/view.php
-
73config/amqp.php
-
221config/app.php
-
43config/cors.php
-
21config/filesystems.php
-
172config/geoip.php
-
98config/logging.php
-
179config/media-library.php
-
38config/query-builder.php
-
40config/services.php
-
19database/factories/BusinessFactory.php
-
19database/factories/CostFactory.php
-
35database/factories/FileFactory.php
-
38database/factories/FingerprintFactory.php
-
23database/factories/ProjectFactory.php
-
16database/factories/SprintflowFactory.php
-
16database/factories/SystemFactory.php
-
15database/factories/TagFactory.php
-
22database/factories/TaskFactory.php
-
21database/factories/TransactionFactory.php
-
55database/factories/UserFactory.php
-
15database/factories/WorkflowFactory.php
-
15database/factories/WorkstatusFactory.php
-
0database/migrations/.gitkeep
-
36database/migrations/2019_08_19_000000_create_failed_jobs_table.php
-
10database/migrations/2020_08_18_085016_create_users_table.php
-
38database/migrations/2020_08_18_085017_fingerprints.php
-
49database/migrations/2020_08_18_085018_create_businesses_table.php
-
58database/migrations/2020_08_18_085046_create_projects_table.php
-
13database/migrations/2020_08_18_085054_create_workflows_table.php
-
39database/migrations/2020_08_18_085102_create_statuses_table.php
-
41database/migrations/2020_08_18_085114_create_tags_table.php
-
34database/migrations/2020_08_28_095802_create_systems_table.php
-
38database/migrations/2020_08_28_101545_create_sprint_table.php
-
37database/migrations/2020_10_31_182018_create_transactions_table.php
-
41database/migrations/2021_09_03_085114_create_costs_table.php
@ -0,0 +1,136 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Console\Commands; |
||||
|
|
||||
|
use DB; |
||||
|
use App\Business; |
||||
|
use Illuminate\Console\Command; |
||||
|
use Illuminate\Support\Facades\Cache; |
||||
|
|
||||
|
class CostCommand extends Command |
||||
|
{ |
||||
|
public const USER_FEE = 400; |
||||
|
|
||||
|
public const FILE_FEE = [ |
||||
|
200 => 0, |
||||
|
400 => 1000, |
||||
|
800 => 2000, |
||||
|
]; |
||||
|
|
||||
|
protected $signature = 'cost:work'; |
||||
|
|
||||
|
protected $description = 'Run the cost worker'; |
||||
|
|
||||
|
public function __construct() |
||||
|
{ |
||||
|
parent::__construct(); |
||||
|
} |
||||
|
|
||||
|
public function handle() |
||||
|
{ |
||||
|
// infinte loop
|
||||
|
while (true) { |
||||
|
// to baghali ha
|
||||
|
$recorded_month = jdate($business->calculated_at)->format("Y-m-01"); |
||||
|
|
||||
|
$calculated_at = Cache::get('calculated_at'); |
||||
|
if ($calculated_at === null) { |
||||
|
$business = Business::orderBy('calculated_at')->first()->load('users', 'cost'); |
||||
|
$calculated_at = $business->calculated_at; |
||||
|
$until_now = jdate()->getMonth() > jdate($business->calculated_at)->getMonth() |
||||
|
? jdate($business->calculated_at)->toCarbon()->setTime("00", "00", "00") |
||||
|
: \Carbon\Carbon::now(); |
||||
|
|
||||
|
Cache::put('calculated_at', $until_now, 60); |
||||
|
} |
||||
|
|
||||
|
// if calculated_at less than an hour stop
|
||||
|
if (\Carbon\Carbon::now()->diffInMinutes($until_now) <= 60) { |
||||
|
$this->info('nothing to cost'); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
DB::beginTransaction(); |
||||
|
|
||||
|
// business order by last_calculated_at take first
|
||||
|
if (!isset($business)) { |
||||
|
$business = Business::orderBy('calculated_at')->first()->load('users', 'cost'); |
||||
|
} |
||||
|
|
||||
|
$user_fee = enum('business.fee.user'); |
||||
|
|
||||
|
// get business employee
|
||||
|
$users_cost = $business->cost |
||||
|
->where('type', '=', 'users') |
||||
|
->where('fee', '=', $user_fee) |
||||
|
->where('month', '=', $recorded_month) |
||||
|
->where('amount', '=', $business->users->count()) |
||||
|
->first(); |
||||
|
|
||||
|
if ($users_cost === null) { |
||||
|
$business->cost()->create([ |
||||
|
'type' => 'users', |
||||
|
'month' => $recorded_month, |
||||
|
'amount' => $business->users->count(), |
||||
|
'fee' => $user_fee, |
||||
|
'duration' => $duration = $until_now->diffInSeconds($calculated_at), // from the created_at time of the newset fifth user
|
||||
|
'additional' => $business->users->pluck('id')->toArray(), |
||||
|
]); |
||||
|
} else { |
||||
|
$users_cost->update([ |
||||
|
'duration' => $duration = $until_now->diffInMinutes($calculated_at), // last calc - (current month - now else last calc - end of the past month),
|
||||
|
'additional' => $business->users->pluck('id')->toArray(), |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
$costs = $user_fee * $duration; |
||||
|
|
||||
|
// do the math in php
|
||||
|
if (intdiv($business->files_volume, 200) === 0) { |
||||
|
$pads = 0; |
||||
|
} else { |
||||
|
$pads = intdiv($business->files_volume, 200) - 1; |
||||
|
} |
||||
|
|
||||
|
$file_fee = enum('business.fee.file'); |
||||
|
|
||||
|
$files = $business->cost |
||||
|
->where('type', '=', 'files') |
||||
|
->where('fee', '=', $file_fee) |
||||
|
->where('month', '=', $recorded_month) |
||||
|
->where('amount', '=', $business->files_volume) |
||||
|
->first(); |
||||
|
|
||||
|
if ($files === null) { |
||||
|
$business->cost()->create([ |
||||
|
'type' => 'files', |
||||
|
'month' => $recorded_month, |
||||
|
'amount' => $pads, |
||||
|
'fee' => $file_fee, |
||||
|
'duration' => $duration = $until_now->diffInMinutes($calculated_at), // how to determine the file?,
|
||||
|
]); |
||||
|
} else { |
||||
|
$files->update([ |
||||
|
'duration' => $duration = $until_now->diffInMinutes($calculated_at), // last calc - (current month - now else last calc - end of the past month),,
|
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
$costs += $file_fee * $duration; |
||||
|
|
||||
|
// increment and decrement of wallet in php
|
||||
|
// deduct costs from your business wallet
|
||||
|
// make sure save the calculated_at
|
||||
|
$business->update([ |
||||
|
'wallet' => $business->wallet - $costs, |
||||
|
'calculated_at' => \Carbon\Carbon::now(), |
||||
|
]); |
||||
|
DB::commit(); |
||||
|
} catch (Throwable $thr) { |
||||
|
DB::rollback(); |
||||
|
throw $thr; |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,268 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\User; |
||||
|
use App\Business; |
||||
|
use App\Fingerprint; |
||||
|
use Illuminate\Support\Str; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
use Illuminate\Http\JsonResponse; |
||||
|
use App\Http\Resources\UserResource; |
||||
|
use Illuminate\Support\Facades\Auth; |
||||
|
use Illuminate\Support\Facades\Hash; |
||||
|
use Illuminate\Support\Facades\Cache; |
||||
|
use Laravel\Lumen\Routing\Controller; |
||||
|
use Laravel\Socialite\Facades\Socialite; |
||||
|
use Illuminate\Session\TokenMismatchException; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
|
||||
|
class AuthController extends Controller |
||||
|
{ |
||||
|
public function redirectToGoogle() |
||||
|
{ |
||||
|
return Socialite::driver('google')->stateless()->redirect(); |
||||
|
} |
||||
|
|
||||
|
public function handleGoogleCallback(Request $request) |
||||
|
{ |
||||
|
try { |
||||
|
|
||||
|
$user = Socialite::driver('google')->stateless()->user(); |
||||
|
$find_user = User::where('email', $user->email)->first(); |
||||
|
|
||||
|
if ($find_user) { |
||||
|
|
||||
|
$find_user->update([ |
||||
|
'active' => true |
||||
|
]); |
||||
|
|
||||
|
Auth::setUser($find_user); |
||||
|
|
||||
|
} else { |
||||
|
|
||||
|
$user = User::create($user->user + [ |
||||
|
'password' => Hash::make('google-login-user'), |
||||
|
'username' => $user->email, |
||||
|
'active' => true |
||||
|
]); |
||||
|
|
||||
|
Auth::setUser($user); |
||||
|
|
||||
|
} |
||||
|
$finger_print = $this->createFingerPrint(); |
||||
|
return redirect('http://localhost:3000/login?token='.$finger_print->token); |
||||
|
|
||||
|
} catch (Exception $e) { |
||||
|
dd($e->getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function login(Request $request) |
||||
|
{ |
||||
|
// todo: Logging in from a new device will result in sending a notification
|
||||
|
$this->validate($request, [ |
||||
|
'email' => 'required|email|exists:users,email', |
||||
|
'password' => 'required|string|min:6' |
||||
|
]); |
||||
|
|
||||
|
$user = User::where('email', $request->email)->first(); |
||||
|
if ($user && Hash::check($request->password, $user->password)) { |
||||
|
Auth::viaRequest('api', fn() => $user); |
||||
|
|
||||
|
return [ |
||||
|
'auth' => $this->createFingerPrint(), |
||||
|
'businesses' => Auth::user()->businesses->keyBy('id')->map(fn($b, $bid) => Business::info($bid)) |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
return new JsonResponse([ |
||||
|
'message' => trans('auth.failed'), |
||||
|
'status' => Response::HTTP_NOT_FOUND, |
||||
|
], Response::HTTP_NOT_FOUND); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function register(Request $request) |
||||
|
{ |
||||
|
$this->validate($request, [ |
||||
|
'name' => 'required|string|max:225|min:2', |
||||
|
'username' => ['required', Rule::unique('users', 'username')], |
||||
|
'email' => ['required', 'email', Rule::unique('users', 'email')], |
||||
|
'password' => 'required|string|min:8' |
||||
|
]); |
||||
|
|
||||
|
$request->merge(['password' => Hash::make($request->password)]); |
||||
|
|
||||
|
$code_data = ['verification_code' => $this->sendVerificationCode()]; |
||||
|
$method_data = ['method' => 'registerMain']; |
||||
|
|
||||
|
Cache::put($request->email, $request->all() + $code_data + $method_data, 3600); // remain one hour
|
||||
|
|
||||
|
return \response()->json([ |
||||
|
'message' => 'Code send for user and user must be verified.'], |
||||
|
Response::HTTP_OK); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function registerMain($user_info) |
||||
|
{ |
||||
|
$user = User::create($user_info); |
||||
|
|
||||
|
Auth::setUser($user); |
||||
|
|
||||
|
return $this->createFingerPrint(); |
||||
|
} |
||||
|
|
||||
|
public function sendVerificationCode($contact_way = null) |
||||
|
{ |
||||
|
$verification_code = 1234; // rand(10001, 99999)
|
||||
|
|
||||
|
//send code for user with contact way
|
||||
|
|
||||
|
return $verification_code; |
||||
|
} |
||||
|
|
||||
|
public function verification(Request $request) |
||||
|
{ |
||||
|
if (!Cache::has($request->email)) { |
||||
|
return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); |
||||
|
} |
||||
|
|
||||
|
$user_info = Cache::get($request->email); |
||||
|
|
||||
|
$this->validate($request, [ |
||||
|
'email' => 'required|email', |
||||
|
'verification_code' => 'required|string|min:4|max:4|in:'.$user_info['verification_code'] |
||||
|
]); |
||||
|
|
||||
|
Cache::forget($request->email); |
||||
|
|
||||
|
return isset($user_info['method']) ? |
||||
|
call_user_func('self::'.$user_info['method'], $user_info) : |
||||
|
\response()->json(['message' => 'Code verified successfully.'], Response::HTTP_OK,); |
||||
|
} |
||||
|
|
||||
|
public function forgetPassword(Request $request) |
||||
|
{ |
||||
|
$this->validate($request, [ |
||||
|
'email' => 'required|email|exists:users,email' |
||||
|
]); |
||||
|
|
||||
|
$code_data = ['verification_code' => $this->sendVerificationCode()]; |
||||
|
|
||||
|
Cache::put($request->email, $request->all() + $code_data, 3600); // remain one hour
|
||||
|
|
||||
|
return \response()->json([ |
||||
|
'message' => 'Code send for user and user must be verified.'], |
||||
|
Response::HTTP_OK); |
||||
|
} |
||||
|
|
||||
|
public function updatePassword(Request $request) |
||||
|
{ |
||||
|
if (!Cache::has($request->email)) { |
||||
|
return \response()->json(['message' => 'Code expired.'], Response::HTTP_BAD_REQUEST); |
||||
|
} |
||||
|
|
||||
|
$this->validate($request, [ |
||||
|
'email' => 'required|email', |
||||
|
'password' => 'required|string|min:8|confirmed', |
||||
|
'verification_code' => 'required|string|min:4|max:4|in:'.Cache::get($request->email)['verification_code'] |
||||
|
]); |
||||
|
|
||||
|
$user = User::where('email', $request->email)->first(); |
||||
|
|
||||
|
$user->update([ |
||||
|
'password' => Hash::make($request->password) |
||||
|
]); |
||||
|
|
||||
|
Auth::setUser($user); |
||||
|
|
||||
|
return $this->createFingerPrint(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param Request $request |
||||
|
* @return mixed |
||||
|
* @throws TokenMismatchException |
||||
|
*/ |
||||
|
public function logout(Request $request) |
||||
|
{ |
||||
|
$token = $request->getCurrentToken(); |
||||
|
if (blank($token)) { |
||||
|
return new JsonResponse([ |
||||
|
'message' => 'Not authorized request.', |
||||
|
'status' => Response::HTTP_UNAUTHORIZED |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/** @var Fingerprint $token */ |
||||
|
$token = Auth::user()->fingerprints()->firstWhere([ |
||||
|
'token' => $token, |
||||
|
]); |
||||
|
|
||||
|
if ($token) { |
||||
|
return $token->delete(); |
||||
|
} |
||||
|
|
||||
|
throw new TokenMismatchException('Invalid token!'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param string $token |
||||
|
* @throws TokenMismatchException |
||||
|
*/ |
||||
|
public function revoke(string $token) |
||||
|
{ |
||||
|
/** @var Fingerprint $token */ |
||||
|
$token = Auth::user()->fingerprints()->firstWhere([ |
||||
|
'token' => $token, |
||||
|
]); |
||||
|
|
||||
|
if ($token) { |
||||
|
return $token->delete(); |
||||
|
} |
||||
|
|
||||
|
throw new TokenMismatchException(); |
||||
|
} |
||||
|
|
||||
|
public function auth() |
||||
|
{ |
||||
|
return new UserResource(Auth::user()); |
||||
|
} |
||||
|
|
||||
|
public function authWithInfo() |
||||
|
{ |
||||
|
return [ |
||||
|
'auth' => new UserResource(Auth::user()), |
||||
|
'businesses' => Auth::user()->businesses->keyBy('id') ->map(fn($b, $bid) => Business::info($bid)) |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function delete(Request $request) |
||||
|
{ |
||||
|
Auth::user()->fingerprints()->delete(); |
||||
|
unset(Auth::user()->token); |
||||
|
Auth::user()->delete(); |
||||
|
|
||||
|
return 'success'; |
||||
|
} |
||||
|
|
||||
|
public function createFingerPrint() |
||||
|
{ |
||||
|
$attributes = [ |
||||
|
'agent' => \request()->getAgent(), |
||||
|
'ip' => \request()->getClientIp(), |
||||
|
'os' => \request()->getOS(), |
||||
|
'latitude' => \request()->getLocation()->getAttribute('lat'), |
||||
|
'longitude' => \request()->getLocation()->getAttribute('lon'), |
||||
|
]; |
||||
|
|
||||
|
$values = [ |
||||
|
'token' => Str::random(60) |
||||
|
]; |
||||
|
|
||||
|
return Auth::user()->fingerprints()->firstOrCreate($attributes, $attributes + $values); |
||||
|
} |
||||
|
} |
@ -0,0 +1,195 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\User; |
||||
|
use App\Business; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Support\Facades\Auth; |
||||
|
use Illuminate\Support\Facades\DB; |
||||
|
|
||||
|
class BusinessController extends Controller |
||||
|
{ |
||||
|
public function index() |
||||
|
{ |
||||
|
return auth()->user()->businesses |
||||
|
->keyBy('id') |
||||
|
->map(fn($b, $bid) => Business::info($bid)); |
||||
|
} |
||||
|
|
||||
|
public function store(Request $request) |
||||
|
{ |
||||
|
// $users = [];
|
||||
|
// foreach ($request->users ?? [] as $key => $value) {
|
||||
|
// $users[$value] = [];
|
||||
|
// }
|
||||
|
|
||||
|
// $owner = [
|
||||
|
// Auth::id() => [
|
||||
|
// 'level' => enum('levels.owner.id'),
|
||||
|
// ]
|
||||
|
// ];
|
||||
|
//
|
||||
|
// $users = $users + $owner;
|
||||
|
//
|
||||
|
// $request->merge(['users' => $users]);
|
||||
|
|
||||
|
$business = Business::create($request->all()); |
||||
|
$business->users()->sync([Auth::id() => [ |
||||
|
'level' => enum('levels.owner.id'), |
||||
|
] |
||||
|
], false); |
||||
|
|
||||
|
return Business::info($business->id); |
||||
|
} |
||||
|
|
||||
|
public function show(string $business) |
||||
|
{ |
||||
|
permit('businessAccess'); |
||||
|
return Business::info($business); |
||||
|
} |
||||
|
|
||||
|
public function update(Request $request, string $business) |
||||
|
{ |
||||
|
// permit('businessEdit');
|
||||
|
$business = Business::findOrFail($business); |
||||
|
$business->fill($request->all())->save(); |
||||
|
|
||||
|
return Business::info($business->id); |
||||
|
} |
||||
|
|
||||
|
public function setAvatar(Request $request, string $business) |
||||
|
{ |
||||
|
$business = Business::findOrFail($business); |
||||
|
if ($request->hasFile('avatar')) { |
||||
|
$business->saveAsAvatar($request->file('avatar')); |
||||
|
} |
||||
|
|
||||
|
return Business::info($business->id); |
||||
|
} |
||||
|
|
||||
|
public function unSetAvatar(Request $request, string $business) |
||||
|
{ |
||||
|
$business = Business::findOrFail($business); |
||||
|
$business->deleteAvatar(); |
||||
|
|
||||
|
return Business::info($business->id); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function info(string $business) |
||||
|
{ |
||||
|
return request('_business_info'); |
||||
|
} |
||||
|
|
||||
|
public function restore(string $business) |
||||
|
{ |
||||
|
$business = Business::onlyTrashed()->findOrFail($business); |
||||
|
$business->restore(); |
||||
|
|
||||
|
return response(['message' => 'business successfully restored.']); |
||||
|
} |
||||
|
|
||||
|
public function storeOrUpdateUser($business, Request $request) |
||||
|
{ |
||||
|
permit('businessUsers'); |
||||
|
$validatedData = $this->validate($request, [ |
||||
|
'level' => 'required|numeric|between:0,4', |
||||
|
'user_id' => 'required|numeric|not_in:'.auth()->id(), |
||||
|
]); |
||||
|
|
||||
|
DB::transaction(function () use ($validatedData, $request, $business) { |
||||
|
$this->addUser($business, $request->user_id, $validatedData); |
||||
|
if (can('businessAccess', ['user_id'=> $request->user_id])) { |
||||
|
//update
|
||||
|
$this->relatedUpdateChanges($request->user_id, $request->level); |
||||
|
} |
||||
|
}, 3); |
||||
|
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function relatedUpdateChanges($user_id, $level) |
||||
|
{ |
||||
|
if ($level == enum('levels.owner.id')) { |
||||
|
// user up level to owner
|
||||
|
$this->removeProjectDirectRelation($user_id); |
||||
|
} |
||||
|
if ($level != enum('levels.owner.id') && |
||||
|
$level > request('_business_info')['info']['users'][$user_id]['level']) { |
||||
|
// user at least up level to $request->level
|
||||
|
$this->updateProjectAccessLevel($level, $user_id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function addUser($business, $user, $validatedData) |
||||
|
{ |
||||
|
$businessModel = Business::findOrFail($business); |
||||
|
$businessModel->users()->sync([$user => $validatedData], false); |
||||
|
} |
||||
|
|
||||
|
public function removeProjectDirectRelation($user) |
||||
|
{ |
||||
|
$userModel = User::findOrFail($user); |
||||
|
return $userModel->projects()->sync([], true); |
||||
|
} |
||||
|
|
||||
|
public function updateProjectAccessLevel($level, $user) |
||||
|
{ |
||||
|
$ids = []; |
||||
|
foreach (request('_business_info')['projects'] as $project_id => $item) { |
||||
|
foreach ($item['members'] as $idx => $member) { |
||||
|
if ($member['id'] == $user && $member['level'] != enum('levels.inactive.id') && $member['level'] < $level) { |
||||
|
$ids[$project_id] = ['level' => $level]; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
$userModel = User::findOrFail($user); |
||||
|
return $userModel->projects()->sync($ids, false); |
||||
|
} |
||||
|
|
||||
|
public function deleteUser($business, $user) |
||||
|
{ |
||||
|
permit('businessAccess'); |
||||
|
$this->checkDeleteUserPolicy($user); |
||||
|
$businessModel = Business::findOrFail($business); |
||||
|
|
||||
|
DB::transaction(function () use ($user, $businessModel) { |
||||
|
$this->detachUser($businessModel, $user); |
||||
|
$this->removeProjectDirectRelation($user); |
||||
|
}, 3); |
||||
|
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function haveAnotherOwner($user) |
||||
|
{ |
||||
|
foreach (request('_business_info')['info']['users'] as $id => $item) { |
||||
|
if ($item['level'] == enum('levels.owner.id') && $id != $user) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function detachUser($business, $user) |
||||
|
{ |
||||
|
return $business->users()->sync( |
||||
|
$business->users->except($user)->pluck('id')->toArray() |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public function checkDeleteUserPolicy($user) |
||||
|
{ |
||||
|
if (!can('isBusinessOwner') && auth()->id() != $user ) { |
||||
|
// Non owner user remove another owner
|
||||
|
abort(405); |
||||
|
} |
||||
|
if (can('isBusinessOwner') && auth()->id() == $user && !$this->haveAnotherOwner($user)) { |
||||
|
// Owner remove self but business haven't another owner
|
||||
|
abort(405); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\Transaction; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Support\Facades\Auth; |
||||
|
use Spatie\QueryBuilder\QueryBuilder; |
||||
|
use Spatie\QueryBuilder\AllowedFilter; |
||||
|
use App\Http\Resources\TransactionResource; |
||||
|
|
||||
|
class CreditController extends Controller |
||||
|
{ |
||||
|
public function payments(Request $request, int $business) |
||||
|
{ |
||||
|
\permit('isBusinessOwner'); |
||||
|
|
||||
|
$query = Transaction::where('business_id', $business); |
||||
|
|
||||
|
$builder = QueryBuilder::for($query) |
||||
|
->allowedSorts([ |
||||
|
'amount', |
||||
|
'succeeded', |
||||
|
'created_at', |
||||
|
]) |
||||
|
->allowedFilters([ |
||||
|
AllowedFilter::exact('user_id'), |
||||
|
AllowedFilter::exact('succeeded'), |
||||
|
]); |
||||
|
|
||||
|
return TransactionResource::collection( |
||||
|
$builder->paginate($request->per_page) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public function pay(Request $request, int $business) |
||||
|
{ |
||||
|
\permit('isBusinessOwner'); |
||||
|
|
||||
|
return Transaction::create([ |
||||
|
'user_id'=> Auth::id(), |
||||
|
'business_id'=> $business, |
||||
|
'amount'=> $request->amount, |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
public function redirection($transaction) |
||||
|
{ |
||||
|
$transaction = Transaction::findOrFail($transaction); |
||||
|
if ($transaction->isWentToPaymentGateway()) { |
||||
|
throw new \Exception("Siktir baba ye bar ghablan rafti."); |
||||
|
} |
||||
|
|
||||
|
return $transaction->prepare()->redirect(); |
||||
|
} |
||||
|
|
||||
|
public function callback(Request $request) |
||||
|
{ |
||||
|
$transaction = Transaction::findByAuthority($request->get('Authority'))->verify(); |
||||
|
if (!$transaction->hasBeenAppliedToWallet() && $transaction->succeeded) { |
||||
|
$transaction->business->increment("wallet", $transaction->amount); |
||||
|
$transaction->amountWasAppliedToWallet(); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
throw new \Exception("تراکنش تایید نشد"); |
||||
|
} |
||||
|
} |
@ -0,0 +1,203 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\File; |
||||
|
use App\Project; |
||||
|
use App\Business; |
||||
|
use App\Rules\maxBound; |
||||
|
use Illuminate\Support\Str; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Http\UploadedFile; |
||||
|
use Illuminate\Support\Facades\Auth; |
||||
|
use App\HiLib\Resources\FileResource; |
||||
|
use Spatie\QueryBuilder\QueryBuilder; |
||||
|
use Spatie\QueryBuilder\AllowedFilter; |
||||
|
use Illuminate\Support\Facades\Storage; |
||||
|
|
||||
|
class FileController extends Controller |
||||
|
{ |
||||
|
public function index(int $business, Request $request) |
||||
|
{ |
||||
|
permit('businessAccess'); |
||||
|
|
||||
|
$this->indexValidation($request); |
||||
|
|
||||
|
$per_page = $request->limit > 100 ? 10 : $request->limit; |
||||
|
|
||||
|
return $this->indexFiltering($business)->paginate($per_page); |
||||
|
} |
||||
|
|
||||
|
public function indexValidation($request) |
||||
|
{ |
||||
|
$bound = 10; |
||||
|
$this->validate($request, [ |
||||
|
'filter.project_id' => [new maxBound($bound)] , |
||||
|
'filter.user_id' => [new maxBound($bound)] , |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
public function indexFiltering($business) |
||||
|
{ |
||||
|
$query = File::where('business_id', $business); |
||||
|
$fileQ = QueryBuilder::for($query) |
||||
|
->allowedFilters([ |
||||
|
AllowedFilter::exact('user_id'), |
||||
|
AllowedFilter::exact('project_id'), |
||||
|
AllowedFilter::exact('extension'), |
||||
|
]); |
||||
|
if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) { |
||||
|
$requested_projects = isset(\request('filter')['project_id']) ? |
||||
|
array_unique(explode(',',\request('filter')['project_id'] ?? null )) : |
||||
|
null; |
||||
|
$requested_projects = collect($requested_projects)->keyBy(null)->toArray(); |
||||
|
$project_ids = $this->myStateProjects($requested_projects); |
||||
|
$fileQ->where(function ($q) use ($project_ids) { |
||||
|
$q->whereIn('project_id', $project_ids['non_guest_ids']) |
||||
|
->orWhere(function ($q) use ($project_ids) { |
||||
|
$q->whereIn('project_id', $project_ids['guest_ids']) |
||||
|
->where('user_id', auth()->id()); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (request()->filled('group')) { |
||||
|
$fileQ->selectRaw("files.group, count(files.id) as file_count, sum(files.size) as file_size")->groupBy('group'); |
||||
|
} |
||||
|
|
||||
|
return $fileQ; |
||||
|
} |
||||
|
|
||||
|
public function myStateProjects($requested_projects) |
||||
|
{ |
||||
|
$non_guest_ids = []; |
||||
|
$guest_ids = []; |
||||
|
$is_empty = empty($requested_projects); |
||||
|
|
||||
|
foreach (\request('_business_info')['info']['projects'] as $p_id => $p) { |
||||
|
|
||||
|
$level = \request('_business_info')['info']['projects'][$p_id]['members'][\auth()->id()]['level']; |
||||
|
|
||||
|
if (( $is_empty || isset($requested_projects[$p_id])) |
||||
|
&& $level > enum('levels.guest.id')) { |
||||
|
array_push($non_guest_ids, $p_id); |
||||
|
} |
||||
|
if (( $is_empty || isset($requested_projects[$p_id])) |
||||
|
&& $level == enum('levels.guest.id')) { |
||||
|
array_push($guest_ids, $p_id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ['non_guest_ids' => $non_guest_ids, 'guest_ids' => $guest_ids]; |
||||
|
} |
||||
|
|
||||
|
public function store(Request $request, int $business, int $project) |
||||
|
{ |
||||
|
// different size and different validation
|
||||
|
// validate
|
||||
|
// validate the wallet is not so much in debt
|
||||
|
// create record in the db
|
||||
|
// put file in s3
|
||||
|
// return file resource
|
||||
|
$business = Business::findOrFail($business); |
||||
|
$project = Project::findOrFail($project); |
||||
|
|
||||
|
// $this->validate($request, ['file' => 'required|file',]);
|
||||
|
|
||||
|
$file = $request->file('file'); |
||||
|
$file_extension = $file->getClientOriginalExtension(); |
||||
|
$file_name = Str::random(40).'.'.$file_extension; |
||||
|
$file->storeAs( |
||||
|
$business->id.\DIRECTORY_SEPARATOR.$project->id, |
||||
|
$file_name, |
||||
|
's3' |
||||
|
); |
||||
|
|
||||
|
$file_record = File::create([ |
||||
|
'user_id' => Auth::id(), |
||||
|
'business_id' => $business->id, |
||||
|
'project_id' => $project->id, |
||||
|
'disk' => 's3', // default disk
|
||||
|
'original_name' => $file->getClientOriginalName(), |
||||
|
'extension' => $file_extension, |
||||
|
'name' => $file_name, |
||||
|
'mime' => $file->getClientMimeType(), |
||||
|
'group' => $this->groupDetection($file), |
||||
|
'size' => $file->getSize(), |
||||
|
'description' => $request->description |
||||
|
]); |
||||
|
|
||||
|
return new FileResource($file_record); |
||||
|
} |
||||
|
|
||||
|
public function groupDetection(UploadedFile $file) |
||||
|
{ |
||||
|
// Media files like mp4, mp3, wma and png or jpeg
|
||||
|
[$type, $subtype] = Str::of($file->getMimeType())->explode("/",2)->pad(2, null); |
||||
|
|
||||
|
if (in_array($type, ['audio', 'video', 'image'])) { |
||||
|
return $type; |
||||
|
} |
||||
|
|
||||
|
// Covert string to \Illuminate\Support\Stringable object
|
||||
|
$subtype = Str::of($subtype); |
||||
|
|
||||
|
// PDF files
|
||||
|
if ($subtype->contains(["pdf"])) { |
||||
|
return "pdf"; |
||||
|
} |
||||
|
|
||||
|
// Compressed files like zip, cab, rar, etc.
|
||||
|
if ($subtype->contains(['compressed']) || in_array($file->getClientOriginalExtension(), ['zip', 'rar','7z','cab'])) { |
||||
|
return "compressed"; |
||||
|
} |
||||
|
|
||||
|
// Office files like xls, xlsx, doc, docx, etc.
|
||||
|
if ($subtype->contains(['vnd.ms', 'vnd.sealed', 'officedocument', 'opendocument'])) { |
||||
|
return "office"; |
||||
|
} |
||||
|
|
||||
|
// Non of the above files
|
||||
|
return "other"; |
||||
|
} |
||||
|
|
||||
|
public function download(int $business, int $project, int $file) |
||||
|
{ |
||||
|
// requested file belongs to this project and this business
|
||||
|
// check permisson
|
||||
|
// create perma link or temp link
|
||||
|
// return the file resource or stream it
|
||||
|
return File::findOrFail($file)->getTemporaryLink(); |
||||
|
} |
||||
|
|
||||
|
public function rename(Request $request, int $business, int $project, int $file) |
||||
|
{ |
||||
|
// requested file belongs to this project and this business
|
||||
|
// check permisson
|
||||
|
// update original name
|
||||
|
// return the file resource
|
||||
|
// sanitize the name for slashs and back slashes
|
||||
|
$this->validate($request, [ |
||||
|
'name' => 'required|string' |
||||
|
]); |
||||
|
|
||||
|
$file = File::findOrFail($file); |
||||
|
|
||||
|
$file->update(['original_name' => $request->name.".".$file->extension]); |
||||
|
|
||||
|
return new FileResource($file); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function delete(Request $request, int $business, int $project, int $file) |
||||
|
{ |
||||
|
// requested file belongs to this project and this business
|
||||
|
// check permisson
|
||||
|
// check it's relations
|
||||
|
// delete the file form File table
|
||||
|
// delete file from s3
|
||||
|
$file = File::findOrFail($file); |
||||
|
Storage::disk('s3')->delete($file->getPath()); |
||||
|
return $file->delete(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\Cost; |
||||
|
use App\Business; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Spatie\QueryBuilder\QueryBuilder; |
||||
|
use Spatie\QueryBuilder\AllowedFilter; |
||||
|
|
||||
|
|
||||
|
class InvoiceController extends Controller |
||||
|
{ |
||||
|
|
||||
|
public function index(Request $request, int $business) |
||||
|
{ |
||||
|
$business = Business::findOrFail($business); |
||||
|
|
||||
|
$builder = Cost::select('month') |
||||
|
->selectRaw("concat_ws('-',business_id,month) as factor_id") |
||||
|
->selectRaw("MIN(created_at) as begin") |
||||
|
->selectRaw("MAX(updated_at) as end") |
||||
|
->selectRaw("sum(cost) as cost") |
||||
|
->selectRaw("sum(tax) as tax") |
||||
|
->where('business_id','=',$business->id) |
||||
|
->groupBy('month','factor_id'); |
||||
|
|
||||
|
$costs = QueryBuilder::for($builder) |
||||
|
->allowedSorts([ |
||||
|
'factor_id', |
||||
|
'begin', |
||||
|
'end', |
||||
|
'cost', |
||||
|
]) |
||||
|
->allowedFilters([ |
||||
|
AllowedFilter::exact('month'), |
||||
|
AllowedFilter::exact('type'), |
||||
|
]); |
||||
|
|
||||
|
return $costs->paginate($request->per_page); |
||||
|
} |
||||
|
|
||||
|
public function indexFiltering($business) |
||||
|
{ |
||||
|
$query = File::where('business_id', $business); |
||||
|
$fileQ = QueryBuilder::for($query) |
||||
|
->allowedFilters([ |
||||
|
AllowedFilter::exact('user_id'), |
||||
|
AllowedFilter::exact('project_id'), |
||||
|
AllowedFilter::exact('extension'), |
||||
|
]); |
||||
|
if (\request('_business_info')['info']['users'][\auth()->id()]['level'] != enum('levels.owner.id')) { |
||||
|
$requested_projects = isset(\request('filter')['project_id']) ? |
||||
|
array_unique(explode(',', \request('filter')['project_id'] ?? null)) : |
||||
|
null; |
||||
|
$requested_projects = collect($requested_projects)->keyBy(null)->toArray(); |
||||
|
$project_ids = $this->myStateProjects($requested_projects); |
||||
|
$fileQ->where(function ($q) use ($project_ids) { |
||||
|
$q->whereIn('project_id', $project_ids['non_guest_ids']) |
||||
|
->orWhere(function ($q) use ($project_ids) { |
||||
|
$q->whereIn('project_id', $project_ids['guest_ids']) |
||||
|
->where('user_id', auth()->id()); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (request()->filled('group')) { |
||||
|
$fileQ->selectRaw("files.group, count(files.id) as file_count, sum(files.size) as file_size")->groupBy('group'); |
||||
|
} |
||||
|
|
||||
|
return $fileQ; |
||||
|
} |
||||
|
|
||||
|
public function show(Request $request, int $business, string $date) |
||||
|
{ |
||||
|
return Cost::where('business_id', '=', $business) |
||||
|
->where("month","=",$date) |
||||
|
->get(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,159 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\Project; |
||||
|
use App\Business; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Support\Facades\DB; |
||||
|
|
||||
|
class ProjectController extends Controller |
||||
|
{ |
||||
|
public function index(Request $request, int $business) |
||||
|
{ |
||||
|
// permit('businessAccess');
|
||||
|
return Project::where('business_id', $business)->get(); |
||||
|
} |
||||
|
|
||||
|
public function store(Request $request, string $business) |
||||
|
{ |
||||
|
permit('businessProjects'); |
||||
|
Project::create($request->merge(['business_id' => $business])->all()); |
||||
|
return Business::info($request->route('business'), true); |
||||
|
} |
||||
|
|
||||
|
public function update(Request $request,string $project) |
||||
|
{ |
||||
|
permit('projectEdit', ['project_id' => $project]); |
||||
|
$project = Project::findOrFail($project); |
||||
|
$project->update($request->except('business_id')); |
||||
|
return Business::info($request->route('business'), true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function delete(Request $request, string $project) |
||||
|
{ |
||||
|
permit('businessProjects'); |
||||
|
$project = Project::findOrFail($project); |
||||
|
$project->delete(); |
||||
|
return Business::info($request->route('business')); |
||||
|
} |
||||
|
|
||||
|
public function restore(Request $request, string $project) |
||||
|
{ |
||||
|
$project = Project::onlyTrashed()->findOrFail($project); |
||||
|
$project->restore(); |
||||
|
|
||||
|
return response(['message' => 'project successfully restored.']); |
||||
|
} |
||||
|
|
||||
|
public function storeOrUpdateUser($business, $project, Request $request) |
||||
|
{ |
||||
|
permit('projectUsers', ['project_id' => $project]); |
||||
|
$validatedData = $this->validate($request, [ |
||||
|
'level' => 'required|numeric|between:1,3', |
||||
|
'user_id' => 'required|numeric|not_in:'.auth()->id(), |
||||
|
]); |
||||
|
|
||||
|
$this->checkAddUserPolicy($request->user_id, $request->level); |
||||
|
|
||||
|
$projectModel = Project::findOrFail($project); |
||||
|
DB::transaction(function () use ($business, $validatedData, $request, $projectModel) { |
||||
|
$projectModel->members()->sync([$request->user_id => $validatedData], false); |
||||
|
|
||||
|
if (!can('businessAccess', ['user_id' => $request->user_id])) { |
||||
|
|
||||
|
// Register user to business with zero level
|
||||
|
//User not exist in the business before
|
||||
|
|
||||
|
$this->addUserWithZeroLevel($request->user_id, $business); |
||||
|
} |
||||
|
}, 3); |
||||
|
|
||||
|
return Business::info($projectModel->business_id, true); |
||||
|
} |
||||
|
|
||||
|
public function checkAddUserPolicy($user, $level) |
||||
|
{ |
||||
|
if (can('businessAccess', ['user_id' => $user]) |
||||
|
&& $level < request('_business_info')['info']['users'][$user]['level']) {// before in business
|
||||
|
abort(405); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function addUserWithZeroLevel($user_id, $business) |
||||
|
{ |
||||
|
$businessModel = Business::findOrFail($business); |
||||
|
return $businessModel->users()->sync([$user_id => [ |
||||
|
'level' => 0, |
||||
|
'user_id' => $user_id |
||||
|
]], false); |
||||
|
} |
||||
|
|
||||
|
public function deleteUser($business, $project, $user) |
||||
|
{ |
||||
|
permit('projectAccess', ['project_id' => $project]); |
||||
|
$this->checkDeleteUserPolicy($user, $project); |
||||
|
|
||||
|
$projectModel = Project::findOrFail($project); |
||||
|
|
||||
|
DB::transaction(function () use ($project, $business, $user, $projectModel) { |
||||
|
$this->detachMember($projectModel, $user); |
||||
|
if (!can('isActiveUser', ['user_id' => $user]) && !$this->haveOneProject($user, $project)) { |
||||
|
|
||||
|
// User level in business is zero
|
||||
|
// And haven't another project then remove it form business
|
||||
|
|
||||
|
$businessModel = Business::findOrFail($business); |
||||
|
$this->detachUser($businessModel, $user); |
||||
|
} |
||||
|
}, 3); |
||||
|
|
||||
|
return Business::info($projectModel->business_id, true); |
||||
|
} |
||||
|
|
||||
|
public function detachMember($project, $user) |
||||
|
{ |
||||
|
return $project->members()->detach($user) ? true : abort(404); |
||||
|
} |
||||
|
|
||||
|
public function detachUser($business, $user) |
||||
|
{ |
||||
|
return $business->users()->detach($user) ? true : abort(404); |
||||
|
} |
||||
|
|
||||
|
public function haveOneProject($user, $project) |
||||
|
{ |
||||
|
foreach (request('_business_info')['info']['projects'] as $id => $item) { |
||||
|
if ($item['members'][$user]['level'] > enum('levels.inactive.id') && $id != $project) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public function checkDeleteUserPolicy($user, $project) |
||||
|
{ |
||||
|
if (!can('isProjectOwner', ['project_id' => $project]) && (auth()->id() != $user) ) { |
||||
|
abort(405); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function setAvatar(Request $request, string $project) |
||||
|
{ |
||||
|
$project = Project::findOrFail($project); |
||||
|
if ($request->hasFile('avatar')) { |
||||
|
$project->saveAsAvatar($request->file('avatar')); |
||||
|
} |
||||
|
|
||||
|
return $project; |
||||
|
} |
||||
|
|
||||
|
public function unSetAvatar(Request $request, string $project) |
||||
|
{ |
||||
|
$project = Project::findOrFail($project); |
||||
|
$project->deleteAvatar(); |
||||
|
|
||||
|
return $project; |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
|
||||
|
use App\Business; |
||||
|
use App\Sprint; |
||||
|
use Illuminate\Http\Request; |
||||
|
|
||||
|
class SprintController extends Controller |
||||
|
{ |
||||
|
|
||||
|
public function store($business, $project, Request $request) |
||||
|
{ |
||||
|
permit('projectSprints', ['project_id' => $project]); |
||||
|
|
||||
|
Sprint::create($request->merge( |
||||
|
['business_id' => $business, 'project_id' => $project] |
||||
|
)->except('_business_info')); |
||||
|
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function update($business, $project, $sprint, Request $request) |
||||
|
{ |
||||
|
permit('projectSprints', ['project_id' => $project]); |
||||
|
|
||||
|
$sprint = Sprint::findOrFail($sprint); |
||||
|
|
||||
|
$sprint->update($request->except('_business_info')); |
||||
|
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function delete($business, $project, $sprint) |
||||
|
{ |
||||
|
permit('projectSprints', ['project_id' => $project]); |
||||
|
|
||||
|
$sprint = Sprint::findOrFail($sprint); |
||||
|
|
||||
|
$sprint->delete(); |
||||
|
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
|
||||
|
use App\Business; |
||||
|
use App\Status; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Http\Response; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
|
||||
|
class StatusController extends Controller |
||||
|
{ |
||||
|
public function store($business, $workflow, Request $request) |
||||
|
{ |
||||
|
permit('businessStatuses'); |
||||
|
Status::create($request->merge(['business_id' => $business, 'workflow_id' => $workflow])->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function update($business, $workflow, $status, Request $request) |
||||
|
{ |
||||
|
permit('businessStatuses'); |
||||
|
$status = Status::findOrFail($status); |
||||
|
$status->update($request->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function delete($business, $workflow, $status) |
||||
|
{ |
||||
|
permit('businessStatuses'); |
||||
|
$status = Status::findOrFail($status); |
||||
|
$status->delete(); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use App\Business; |
||||
|
use App\System; |
||||
|
use Illuminate\Http\Request; |
||||
|
|
||||
|
class SystemController extends Controller |
||||
|
{ |
||||
|
public function store($business, $project, Request $request) |
||||
|
{ |
||||
|
permit('projectSystems', ['project_id' => $project]); |
||||
|
System::create([ |
||||
|
'business_id' => $business, |
||||
|
'project_id' => $project, |
||||
|
'name' => $request->name |
||||
|
]); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function update($business, $project, $system, Request $request) |
||||
|
{ |
||||
|
permit('projectSystems', ['project_id' => $project]); |
||||
|
$system = System::findOrFail($system); |
||||
|
$system->update($request->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function delete($business, $project, $system) |
||||
|
{ |
||||
|
permit('projectSystems', ['project_id' => $project]); |
||||
|
$system = System::findOrFail($system); |
||||
|
$system->delete(); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
|
||||
|
use App\Business; |
||||
|
use App\Tag; |
||||
|
use App\Workflow; |
||||
|
use Illuminate\Http\Request; |
||||
|
|
||||
|
class TagController extends Controller |
||||
|
{ |
||||
|
public function store($business, Request $request) |
||||
|
{ |
||||
|
permit('businessTags'); |
||||
|
Tag::create($request->merge(['business_id' => $business])->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function update($business, $tag, Request $request) |
||||
|
{ |
||||
|
permit('businessTags'); |
||||
|
$tag = Tag::findOrFail($tag); |
||||
|
$tag->update($request->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function delete($business, $tag) |
||||
|
{ |
||||
|
permit('businessTags'); |
||||
|
$tag = Tag::findOrFail($tag); |
||||
|
$tag->delete(); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,89 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
use Auth; |
||||
|
use App\File; |
||||
|
use App\Task; |
||||
|
use App\Project; |
||||
|
use App\Business; |
||||
|
use Illuminate\Http\Exceptions\HttpResponseException; |
||||
|
use Illuminate\Http\Request; |
||||
|
use App\Http\Controllers\Controller; |
||||
|
use App\Http\Resources\FileResource; |
||||
|
use mysql_xdevapi\Exception; |
||||
|
use Symfony\Component\HttpFoundation\Response; |
||||
|
|
||||
|
class TaskFileController extends Controller |
||||
|
{ |
||||
|
public function checkBelonging(int $business, int $project, int $task) |
||||
|
{ |
||||
|
$business = Business::findOrFail($business); |
||||
|
$project = Project::findOrFail($project); |
||||
|
$task = Task::find($task); |
||||
|
|
||||
|
if ( |
||||
|
$business->id !== $project->business_id |
||||
|
|| $project->id !== $task['project_id'] |
||||
|
// || $task['user_id']!== Auth::id()
|
||||
|
) { |
||||
|
\abort(Response::HTTP_UNAUTHORIZED); |
||||
|
} |
||||
|
|
||||
|
return [$business, $project, $task]; |
||||
|
} |
||||
|
|
||||
|
public function index(int $business, int $project, int $task) |
||||
|
{ |
||||
|
// check permissions
|
||||
|
// owner project
|
||||
|
// admin project
|
||||
|
// colleague project
|
||||
|
// guest or de active
|
||||
|
// return files as file resource
|
||||
|
[$business, $project, $task] = $this->checkBelonging($business, $project, $task); |
||||
|
return FileResource::collection($task->files); |
||||
|
} |
||||
|
|
||||
|
public function sync(Request $request,int $business, int $project, int $task) |
||||
|
{ |
||||
|
// different size and different validation
|
||||
|
// validate
|
||||
|
// validate the wallet is not so much in debt
|
||||
|
// create record in the db
|
||||
|
// put file in s3
|
||||
|
// return file resource
|
||||
|
[$business, $project, $task] = $this->checkBelonging($business,$project,$task); |
||||
|
|
||||
|
$this->validate($request, [ |
||||
|
'files' => 'required|array', |
||||
|
'files.*' => 'int', |
||||
|
]); |
||||
|
|
||||
|
$files = File::find($request->files)->each(function (File $file) { |
||||
|
if ($file->user_id !== Auth::id()) { |
||||
|
abort(Response::HTTP_UNAUTHORIZED); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// sync
|
||||
|
|
||||
|
return FileResource::collection($files); |
||||
|
} |
||||
|
|
||||
|
public function download(int $business, int $project, int $task, int $file) |
||||
|
{ |
||||
|
// requested file belongs to this project and this business
|
||||
|
// check permisson
|
||||
|
// create perma link or temp link
|
||||
|
// return the file resource or stream it
|
||||
|
[$business, $project, $task] = $this->checkBelonging($business, $project, $task); |
||||
|
|
||||
|
$file = File::find($file); |
||||
|
if ($file->user_id !== Auth::id()) { |
||||
|
abort(Response::HTTP_UNAUTHORIZED); |
||||
|
} |
||||
|
|
||||
|
return $file->getTemporaryLink(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
|
||||
|
use App\User; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Spatie\QueryBuilder\AllowedFilter; |
||||
|
use Spatie\QueryBuilder\QueryBuilder; |
||||
|
|
||||
|
class UserController extends Controller |
||||
|
{ |
||||
|
public function search(Request $request) |
||||
|
{ |
||||
|
$limit = 20; |
||||
|
$userQ = User::query(); |
||||
|
if ($request->filled('search')){ |
||||
|
$userQ = $userQ->where(function($query) use ($request) { |
||||
|
$query->where('email', 'like', '%'.$request->search.'%') |
||||
|
->orWhere('username', 'like', '%'.$request->search.'%'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return $userQ->where('id', '!=', auth()->id()) |
||||
|
->whereNotIn('id', request('_business_info')['info']['users']->keys()) |
||||
|
->select('id', 'name', 'email', 'username')->take($limit)->get(); |
||||
|
} |
||||
|
|
||||
|
public function index(Request $request) |
||||
|
{ |
||||
|
$userQ = QueryBuilder::for(User::class) |
||||
|
->allowedFilters([ |
||||
|
AllowedFilter::exact('id'), |
||||
|
]); |
||||
|
return $userQ->select('id', 'name', 'email', 'username')->get(); |
||||
|
} |
||||
|
|
||||
|
public function show($user) |
||||
|
{ |
||||
|
return User::select('id', 'name', 'email', 'username')->findOrFail($user); |
||||
|
} |
||||
|
|
||||
|
public function update(Request $request, string $user) |
||||
|
{ |
||||
|
$user = User::findOrFail($user); |
||||
|
$user->update($request->all()); |
||||
|
|
||||
|
return $user; |
||||
|
} |
||||
|
|
||||
|
public function setAvatar(Request $request, string $user) |
||||
|
{ |
||||
|
$user = User::findOrFail($user); |
||||
|
if ($request->hasFile('avatar')) { |
||||
|
$user->saveAsAvatar($request->file('avatar')); |
||||
|
} |
||||
|
|
||||
|
return $user; |
||||
|
} |
||||
|
|
||||
|
public function unSetAvatar(Request $request, string $user) |
||||
|
{ |
||||
|
$user = User::findOrFail($user); |
||||
|
$user->deleteAvatar(); |
||||
|
|
||||
|
return $user; |
||||
|
} |
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Controllers; |
||||
|
|
||||
|
|
||||
|
use App\Business; |
||||
|
use App\Status; |
||||
|
use App\Workflow; |
||||
|
use Illuminate\Http\Request; |
||||
|
use Illuminate\Support\Facades\DB; |
||||
|
|
||||
|
class WorkflowController extends Controller |
||||
|
{ |
||||
|
public function store($business, Request $request) |
||||
|
{ |
||||
|
permit('businessWorkFlows'); |
||||
|
Workflow::create($request->merge(['business_id' => $business])->except('_business_info')); |
||||
|
// $statuses = collect($validatedData['statuses'])->map(function($status) use ($workflow, $business) {
|
||||
|
// $status['business_id'] = $business;
|
||||
|
// $status['workflow_id'] = $workflow->id;
|
||||
|
// return $status;
|
||||
|
// });
|
||||
|
// $statuses = $workflow->statuses()->createMany($statuses->toArray());
|
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function update($business, $workflow, Request $request) |
||||
|
{ |
||||
|
permit('businessWorkFlows'); |
||||
|
$workflowModel = Workflow::findOrFail($workflow); |
||||
|
$workflowModel->update($request->except('_business_info')); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
public function syncStatus($business, $workflowModel) |
||||
|
{ |
||||
|
$old_statuses_name = array_keys(collect(\request('_business_info')['workflows'][$workflowModel->id]['statuses'])->keyBy('name')->toArray()); |
||||
|
$new_statuses_name = array_keys(collect(\request('statuses'))->keyBy('name')->toArray()); |
||||
|
$removed_statuses_name = array_diff(array_merge($old_statuses_name, $new_statuses_name), $new_statuses_name); |
||||
|
|
||||
|
foreach ($removed_statuses_name as $status_name) { |
||||
|
//delete all statuses that removed name's from request->statuses
|
||||
|
$workflowModel->statuses()->where('name', $status_name)->first()->delete(); |
||||
|
} |
||||
|
|
||||
|
foreach (request('statuses') as $status) { |
||||
|
//sync another statuses
|
||||
|
$workflowModel->statuses() |
||||
|
->updateOrCreate( |
||||
|
['name' => $status['name'], 'business_id' => $business, 'workflow_id' => $workflowModel->id], |
||||
|
['state' => $status['state'], 'order' => $status['order']] |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return $workflowModel; |
||||
|
} |
||||
|
|
||||
|
public function delete($business, $workflow) |
||||
|
{ |
||||
|
permit('businessWorkFlows'); |
||||
|
$workflow = Workflow::findOrFail($workflow); |
||||
|
foreach ($workflow->statuses as $status) { |
||||
|
//delete all statuses related to this workflow
|
||||
|
$status->delete(); |
||||
|
} |
||||
|
$workflow->delete(); |
||||
|
return Business::info($business, true); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
|
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class BusinessResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'businesses', |
||||
|
]; |
||||
|
|
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'name': |
||||
|
case 'slug': |
||||
|
case 'wallet': |
||||
|
case 'files_volume': |
||||
|
case 'cache': |
||||
|
case 'calculated_at': |
||||
|
case 'created_at': |
||||
|
case 'updated_at': |
||||
|
case 'deleted_at': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
|
||||
|
|
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class FileResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'files', |
||||
|
]; |
||||
|
|
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'user_id': |
||||
|
case 'business_id': |
||||
|
case 'project_id': |
||||
|
case 'disk': |
||||
|
case 'original_name': |
||||
|
case 'name': |
||||
|
case 'extension': |
||||
|
case 'mime': |
||||
|
case 'size': |
||||
|
case 'description': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
|
||||
|
|
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class FingerprintResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'fingerprints', |
||||
|
]; |
||||
|
|
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'id': |
||||
|
case 'agent': |
||||
|
case 'ip': |
||||
|
case 'os': |
||||
|
case 'latitude': |
||||
|
case 'longitude': |
||||
|
case 'token': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
|
||||
|
|
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class ProjectResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'project', |
||||
|
]; |
||||
|
|
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'name': |
||||
|
case 'business_id': |
||||
|
case 'slug': |
||||
|
case 'private': |
||||
|
case 'budget': |
||||
|
case 'start': |
||||
|
case 'finish': |
||||
|
case 'created_at': |
||||
|
case 'updated_at': |
||||
|
case 'archived_at': |
||||
|
case 'deleted_at': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
|
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class TransactionResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'transactions', |
||||
|
]; |
||||
|
|
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'user_id': |
||||
|
case 'business_id': |
||||
|
case 'amount': |
||||
|
case 'succeeded': |
||||
|
case 'options': |
||||
|
case 'created_at': |
||||
|
case 'updated_at': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Http\Resources; |
||||
|
use Illuminate\Http\Resources\Json\JsonResource; |
||||
|
|
||||
|
class UserResource extends JsonResource |
||||
|
{ |
||||
|
public function toArray($request) |
||||
|
{ |
||||
|
$resource = [ |
||||
|
'_service' => 'user', |
||||
|
'_resource' => 'user', |
||||
|
]; |
||||
|
foreach ($this->getAttributes() as $attribute => $value) { |
||||
|
switch ($attribute) { |
||||
|
case 'id': |
||||
|
case 'name': |
||||
|
case 'mobile': |
||||
|
case 'email': |
||||
|
case 'created_at': |
||||
|
case 'updated_at': |
||||
|
$resource[$attribute] = $value; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
$resource['includes']['fingerprints'] = $this->whenLoaded('fingerprints', function () { |
||||
|
return FingerprintResource::collection($this->fingerprints); |
||||
|
}); |
||||
|
|
||||
|
return $resource; |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\RemoteModel; |
||||
|
|
||||
|
class Activity extends RemoteModel |
||||
|
{ |
||||
|
|
||||
|
} |
@ -0,0 +1,486 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\File; |
||||
|
use App\SoftDeletes; |
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
use Illuminate\Http\UploadedFile; |
||||
|
use Spatie\MediaLibrary\HasMedia; |
||||
|
use Illuminate\Support\Facades\Cache; |
||||
|
use App\HiLib\Models\ReportableRelation; |
||||
|
use Spatie\MediaLibrary\InteractsWithMedia; |
||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media; |
||||
|
|
||||
|
class Business extends Model implements HasMedia |
||||
|
{ |
||||
|
use SoftDeletes, |
||||
|
InteractsWithMedia; |
||||
|
|
||||
|
|
||||
|
const CONVERSION_NAME = 'avatar'; |
||||
|
|
||||
|
const COLLECTION_NAME = 'avatars'; |
||||
|
|
||||
|
public static $permissions = ['level']; |
||||
|
|
||||
|
protected $table = 'businesses'; |
||||
|
|
||||
|
protected $fillable = ['name', 'slug', 'wallet','files_volume','cache', 'color', 'description', 'has_avatar', 'calculated_at', 'users']; |
||||
|
|
||||
|
protected $fillable_relations = [ |
||||
|
'users' |
||||
|
]; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name', 'slug', 'wallet', 'files_volume', 'cache', 'calculated_at', // fields
|
||||
|
['users' => 'business_user'] // relations [ name => pivot ]
|
||||
|
]; |
||||
|
|
||||
|
public $detach_relation = false; |
||||
|
|
||||
|
protected $casts = [ |
||||
|
'has_avatar' => 'boolean', |
||||
|
]; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->id, |
||||
|
'workflow_id' => null, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'status_id' => null, |
||||
|
'system_id' => null, |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'bail|required|string|min:2|max:255', |
||||
|
'color' => 'nullable|string|min:2|max:255', |
||||
|
'slug' => ['bail', 'required', 'string', 'min:2', 'max:255', 'regex:/^[a-zA-Z]+[a-zA-Z\d-]*(.*[a-zA-Z\d])+$/', |
||||
|
Rule::unique($this->table, 'slug')->ignore($this->id)], |
||||
|
'description' => 'nullable|string|min:2|max:1000', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function cost() |
||||
|
{ |
||||
|
return $this->hasMany(Cost::class, 'business_id','id'); |
||||
|
} |
||||
|
|
||||
|
public function owners() |
||||
|
{ |
||||
|
return $this->users()->wherePivot('level', '=', enum('levels.owner.id')); |
||||
|
} |
||||
|
|
||||
|
public function members() |
||||
|
{ |
||||
|
return $this->users()->wherePivot('owner', '!=', enum('levels.owner.id')); |
||||
|
} |
||||
|
|
||||
|
public function users() |
||||
|
{ |
||||
|
return $this->belongsToMany( |
||||
|
User::class, 'business_user', 'business_id', 'user_id', |
||||
|
'id', 'id', __FUNCTION__ |
||||
|
) |
||||
|
->using(ReportableRelation::class) |
||||
|
->withPivot(self::$permissions); |
||||
|
} |
||||
|
|
||||
|
public function tags() |
||||
|
{ |
||||
|
return $this->hasMany(Tag::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function projects() |
||||
|
{ |
||||
|
return $this->hasMany(Project::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function systems() |
||||
|
{ |
||||
|
return $this->hasMany(System::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function workflows() |
||||
|
{ |
||||
|
return $this->hasMany(Workflow::class, 'business_id', 'id'); |
||||
|
} |
||||
|
public function sprints() |
||||
|
{ |
||||
|
return $this->hasMany(Sprint::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function statuses() |
||||
|
{ |
||||
|
return $this->hasMany(Status::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function files() |
||||
|
{ |
||||
|
return $this->hasMany(File::class, 'user_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function transactions() |
||||
|
{ |
||||
|
return $this->hasMany(Transaction::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
// users relations
|
||||
|
if (!empty($this->filled_relations['users']) || $this->detach_relation) { |
||||
|
$this->dirties['users'] = $this->users()->sync($this->filled_relations['users'], $this->detach_relation); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static function info($businessId, $fresh = false) |
||||
|
{ |
||||
|
|
||||
|
$info = []; |
||||
|
|
||||
|
$fresh = true; |
||||
|
if ($fresh){ |
||||
|
Cache::forget('business_info'.$businessId); |
||||
|
} |
||||
|
|
||||
|
if (Cache::has('business_info'.$businessId)) { |
||||
|
return Cache::get('business_info'.$businessId); |
||||
|
} else { |
||||
|
$business = self::findOrFail($businessId); |
||||
|
|
||||
|
$tags = $business->tags()->select('id', 'label', 'color')->get()->keyBy('id'); |
||||
|
$info['tags'] = $tags->pluck('id'); |
||||
|
|
||||
|
$workflows = $business->workflows()->select ('id', 'business_id', 'name','desc')->get()->keyBy('id') |
||||
|
->load(['statuses'=> fn($q) => $q->select('id', 'business_id', 'workflow_id', 'name', 'state', 'order')]) |
||||
|
->map(fn($q) => [ |
||||
|
'id' => $q->id, |
||||
|
'business_id' => $q->business_id, |
||||
|
'name' => $q->name, |
||||
|
'desc' => $q->desc, |
||||
|
'statuses' => $q['statuses']->keyBy('id'), |
||||
|
]); |
||||
|
|
||||
|
$info['workflows'] = $workflows->map(fn($q) => ['statuses' => $q['statuses']->pluck('id')]); |
||||
|
|
||||
|
|
||||
|
$users = $business->users()->select('id', 'name', 'email', 'mobile', 'username')->with('media')->get() |
||||
|
->keyBy('id') |
||||
|
->map(fn($u) => [ |
||||
|
'id' => $u->id, |
||||
|
'name' => $u->name, |
||||
|
'email' => $u->email, |
||||
|
'mobile' => $u->mobile, |
||||
|
'username' => $u->get, |
||||
|
'avatar' => $u->has_avatar, |
||||
|
'level' => $u->pivot['level'], |
||||
|
]); |
||||
|
|
||||
|
$info['users'] = $users->map(fn($u) => [ |
||||
|
'level' => $u['level'] |
||||
|
]); |
||||
|
|
||||
|
$projects = $business->projects()->get()->keyBy('id')->load([ |
||||
|
'members' => fn($q) => $q->select('id', 'level'), |
||||
|
'systems' => fn($q) => $q->select('id', 'project_id', 'name'), |
||||
|
'sprints' => fn($q) => $q->select('id', 'project_id', 'name', 'description', 'started_at', 'ended_at', 'active'), |
||||
|
'media', |
||||
|
]); |
||||
|
|
||||
|
$info['projects'] = $projects->map(function($q) use($users){ |
||||
|
return [ |
||||
|
'avatar' => $q->has_avatar, |
||||
|
'systems' => $q->systems->pluck('id'), |
||||
|
'sprints' => $q->sprints->pluck('id'), |
||||
|
'members' => $users->keyBy('id')->map(function($u, $uid) use ($q){ |
||||
|
$project_user = $q->members->firstWhere('id', $uid); |
||||
|
return $project_user? ['level' => $project_user->pivot->level, 'is_direct' => true]: |
||||
|
['level' => $q->private && $u['level'] != enum('levels.owner.id') ? enum('levels.inactive.id') : $u['level'], |
||||
|
'is_direct'=> $project_user ? true : false]; |
||||
|
}) |
||||
|
]; |
||||
|
}); |
||||
|
|
||||
|
$business_info = array_merge( |
||||
|
$business->only('id', 'name', 'slug', 'color','wallet', 'files_volume'), |
||||
|
['avatar' => $business->has_avatar], |
||||
|
compact( |
||||
|
'info', |
||||
|
'tags', |
||||
|
'workflows', |
||||
|
'users', |
||||
|
'projects' |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
Cache::put('business_info'.$businessId , $business_info, config('app.cache_ttl')); |
||||
|
|
||||
|
return $business_info; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static function stats() |
||||
|
{ |
||||
|
return [ |
||||
|
'users' => [ |
||||
|
10 => [ |
||||
|
'statuses' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
'workflows' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
'tags' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
'project' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
|
||||
|
'sprints' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
'works' => [ |
||||
|
], |
||||
|
'__subsystems' => [ |
||||
|
10 => 20, |
||||
|
30 => 40 |
||||
|
], |
||||
|
] |
||||
|
], |
||||
|
'workflows' => [ |
||||
|
50 => [ |
||||
|
'statuses' => [ |
||||
|
20 => 50 |
||||
|
], |
||||
|
|
||||
|
] |
||||
|
], |
||||
|
'statuses' => [ |
||||
|
10 => [ |
||||
|
'users' => [ |
||||
|
|
||||
|
], |
||||
|
'projects' => [ |
||||
|
|
||||
|
] |
||||
|
] |
||||
|
], |
||||
|
'sprints' => [ |
||||
|
10 => [ |
||||
|
'statuses' => [ |
||||
|
10 => [ |
||||
|
10 => 1 |
||||
|
] |
||||
|
] |
||||
|
] |
||||
|
] |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function reportActivity() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static function nuxtInfo($businessId) |
||||
|
{ |
||||
|
if (empty($businessId)){ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
Cache::forget('business_nuxt_info' . $businessId); |
||||
|
return Cache::rememberForever('business_nuxt_info' . $businessId, function () use ($businessId) { |
||||
|
|
||||
|
$business = self::with([ |
||||
|
'projects.members' => fn($q) => $q->select('id', 'name'), |
||||
|
'projects', |
||||
|
'tags', |
||||
|
'workflows.statuses', |
||||
|
'workflows', |
||||
|
'statuses', |
||||
|
'users', |
||||
|
])->findOrFail($businessId); |
||||
|
|
||||
|
|
||||
|
$globals = []; |
||||
|
$business->users->each(function ($u) use (&$globals) { |
||||
|
$globals[$u->id] = $u->pivot->owner == true ? ['owner' => true] : $u->pivot->only(self::$permissions); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
$projects = []; |
||||
|
$business->projects->each(function ($p) use (&$projects, &$globals) { |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
|
||||
|
$business->setRelation('projects', collect($projects)); |
||||
|
$business->setRelation('users', collect($globals)); |
||||
|
|
||||
|
return $business; |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static function infoOld($businessId) |
||||
|
{ |
||||
|
|
||||
|
if (empty($businessId)) |
||||
|
return null; |
||||
|
|
||||
|
|
||||
|
Cache::forget('business_info' . $businessId); |
||||
|
return Cache::rememberForever('business_info' . $businessId, function () use ($businessId) { |
||||
|
$ob = []; |
||||
|
$business = self::with([ |
||||
|
'projects.members' => fn($q) => $q->select('id', 'name'), |
||||
|
'projects' => fn($q) => $q->select('id', 'business_id', 'private'), |
||||
|
'tags' => fn($q) => $q->select('id', 'business_id', 'label'), |
||||
|
'sprints' => fn($q) => $q->select('id','business_id','name', 'active'), |
||||
|
'workflows.statuses' => fn($q) => $q->select('id', 'name'), |
||||
|
'workflows' => fn($q) => $q->select('id', 'business_id', 'name'), |
||||
|
'statuses' => fn($q) => $q->select('id', 'business_id', 'name', 'state'), |
||||
|
'users' => fn($q) => $q->select('id', 'name'), |
||||
|
])->findOrFail($businessId); |
||||
|
|
||||
|
|
||||
|
// permissions in business
|
||||
|
$permissions = []; |
||||
|
$business->users->each(function ($user) use (&$permissions) { |
||||
|
$permissions[$user->id] = $user->pivot->only(['owner']); |
||||
|
$permissions[$user->id]['global_PA'] = $permissions[$user->id]['owner'] == true ? |
||||
|
enum('roles.manager.id') : $user->pivot->PA; |
||||
|
$permissions[$user->id]['global_FA'] = $permissions[$user->id]['owner'] == true ? |
||||
|
enum('roles.manager.id') : $user->pivot->FA; |
||||
|
$permissions[$user->id]['PA'] = []; |
||||
|
$permissions[$user->id]['FA'] = []; |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
//projects
|
||||
|
$projects = []; |
||||
|
$business->projects->each(function ($p) use (&$permissions, $business, &$projects) { |
||||
|
|
||||
|
$business->users->each(function ($user) use (&$permissions, $p) { |
||||
|
$PA = null; |
||||
|
$FA = null; |
||||
|
|
||||
|
if ($permissions[$user->id]['owner'] == true) { |
||||
|
$PA = enum('roles.manager.id'); |
||||
|
$FA = enum('roles.manager.id'); |
||||
|
} else if (!empty($project_user = $p->getPermissions($user->id))) { |
||||
|
$PA = $project_user->pivot->PA; |
||||
|
$FA = $project_user->pivot->FA; |
||||
|
} else if (empty($p->getPermissions($user->id))) { |
||||
|
$PA = $p->private == false ? $permissions[$user->id]['global_PA'] : enum('roles.hidden.id') ; |
||||
|
$FA = $p->private == false ? $permissions[$user->id]['global_FA'] : enum('roles.hidden.id'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
if ($PA !== null && $FA !== null) { |
||||
|
$permissions[$user->id]['PA'][$p->id] = $PA; |
||||
|
$permissions[$user->id]['FA'][$p->id] = $FA; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$projects[$p->id] =''; |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
//workflow
|
||||
|
$workflows = []; |
||||
|
$business->workflows->each(function ($w) use (&$workflows) { |
||||
|
$workflows[$w->id] = $w->statuses->pluck('id'); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
$ob['tags'] = $business->tags->pluck('id'); |
||||
|
$ob['statuses'] = $business->statuses; |
||||
|
$ob['sprints'] = $business->sprints; |
||||
|
$ob['workflows'] = $workflows; |
||||
|
$ob['users'] = $permissions; |
||||
|
$ob['projects'] = $projects; |
||||
|
|
||||
|
return collect($ob); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function registerMediaCollections(): void |
||||
|
{ |
||||
|
$this->addMediaCollection(static::COLLECTION_NAME) |
||||
|
->acceptsMimeTypes([ |
||||
|
'image/jpeg', |
||||
|
'image/png', |
||||
|
'image/tiff', |
||||
|
'image/gif', |
||||
|
]) |
||||
|
->useDisk('public') |
||||
|
->singleFile(); |
||||
|
} |
||||
|
|
||||
|
public function registerMediaConversions(Media $media = null): void |
||||
|
{ |
||||
|
$this->addMediaConversion(static::CONVERSION_NAME) |
||||
|
->width(200) |
||||
|
->height(200) |
||||
|
->queued() |
||||
|
->nonOptimized() |
||||
|
->performOnCollections(static::COLLECTION_NAME); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function saveAsAvatar(UploadedFile $avatar): void |
||||
|
{ |
||||
|
$this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME); |
||||
|
$this->update([ |
||||
|
'has_avatar' => true, |
||||
|
]); |
||||
|
@unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath()); |
||||
|
} |
||||
|
|
||||
|
public function deleteAvatar(): void |
||||
|
{ |
||||
|
$path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath(); |
||||
|
$this->getFirstMedia(static::COLLECTION_NAME)->delete(); |
||||
|
$this->update([ |
||||
|
'has_avatar' => false, |
||||
|
]); |
||||
|
@unlink($path); |
||||
|
} |
||||
|
|
||||
|
public function getAvatarUrl(): ?string |
||||
|
{ |
||||
|
if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) { |
||||
|
return $url; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use Illuminate\Database\Eloquent\Model; |
||||
|
|
||||
|
class Cost extends Model |
||||
|
{ |
||||
|
public $perPage = 12; |
||||
|
|
||||
|
protected $fillable = [ |
||||
|
'business_id', 'type', 'month', 'amount', 'fee', 'duration','cost','tax', 'additional' |
||||
|
]; |
||||
|
|
||||
|
public $casts = [ |
||||
|
'additional' => 'array', |
||||
|
'tax' => 'float', |
||||
|
]; |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Support\Facades\Storage; |
||||
|
|
||||
|
class File extends Model |
||||
|
{ |
||||
|
protected $table = 'files'; |
||||
|
|
||||
|
protected $fillable = [ |
||||
|
'user_id', 'business_id', 'project_id', 'disk', 'original_name', 'name', |
||||
|
'extension', 'mime', 'group','size', 'description' |
||||
|
]; |
||||
|
|
||||
|
protected $casts = []; |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'user_id' => 'required', |
||||
|
'business_id' => 'required', |
||||
|
'project_id' => 'required', |
||||
|
'original_name' => 'required', |
||||
|
'name' => 'required', |
||||
|
'extension' => 'required', |
||||
|
'size' => 'required', |
||||
|
'description' => 'nullable', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function user() |
||||
|
{ |
||||
|
return $this->belongsTo(User::class, 'user_id','id','id',__FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id', 'id', 'id', __FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function project() |
||||
|
{ |
||||
|
return $this->belongsTo(Project::class, 'project_id', 'id', 'id', __FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function reportActivity() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function getPath() |
||||
|
{ |
||||
|
return $this->business->id . \DIRECTORY_SEPARATOR . $this->project->id . DIRECTORY_SEPARATOR . $this->name; |
||||
|
} |
||||
|
|
||||
|
public function getTemporaryLink() |
||||
|
{ |
||||
|
return Storage::disk('s3')->temporaryUrl( |
||||
|
$this->getPath(), |
||||
|
\Carbon\Carbon::now()->addMinutes(15), |
||||
|
[ |
||||
|
'Content-Type' => $this->mime, |
||||
|
'Content-Disposition' => $this->original_name, |
||||
|
] |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
|
||||
|
class Fingerprint extends Model |
||||
|
{ |
||||
|
protected $fillable = ['user_id', 'agent', 'ip', 'os', 'latitude', 'longitude', 'token',]; |
||||
|
|
||||
|
protected $table = 'fingerprints'; |
||||
|
|
||||
|
public function user() |
||||
|
{ |
||||
|
return $this->belongsTo(User::class, 'user_id', 'id', __FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'user_id' => 'required|integer|exists:users,id', |
||||
|
'agent' => 'required|string', |
||||
|
'ip' => 'required|ip', |
||||
|
'os' => 'required|string', |
||||
|
'latitude' => 'required', |
||||
|
'longitude' => 'required', |
||||
|
'token' => 'required|string|min:60', |
||||
|
]; |
||||
|
} |
||||
|
} |
@ -0,0 +1,204 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\File; |
||||
|
use App\SoftDeletes; |
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
use Illuminate\Http\UploadedFile; |
||||
|
use Spatie\MediaLibrary\HasMedia; |
||||
|
use App\HiLib\Models\ReportableRelation; |
||||
|
use Spatie\MediaLibrary\InteractsWithMedia; |
||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media; |
||||
|
|
||||
|
class Project extends Model implements HasMedia |
||||
|
{ |
||||
|
use SoftDeletes, |
||||
|
InteractsWithMedia; |
||||
|
|
||||
|
|
||||
|
const CONVERSION_NAME = 'avatar'; |
||||
|
|
||||
|
const COLLECTION_NAME = 'avatars'; |
||||
|
|
||||
|
public static $permissions = ['level']; |
||||
|
|
||||
|
|
||||
|
protected $table = 'projects'; |
||||
|
|
||||
|
protected $fillable = [ |
||||
|
'business_id', 'name', 'slug', 'private', 'budget', 'color', 'active', 'description', 'has_avatar', 'start', 'finish','members' |
||||
|
]; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'business_id', 'name', 'slug', ['members' => 'project_user'] |
||||
|
]; |
||||
|
|
||||
|
protected $fillable_relations = ['members']; |
||||
|
|
||||
|
public $detach_relation = false; |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'required|string|min:2|max:225', |
||||
|
'slug' => ['required', 'string', 'min:2', 'max:225', |
||||
|
Rule::unique($this->table, 'slug') |
||||
|
->where('business_id', request('business_id')) |
||||
|
->whereNull('deleted_at') |
||||
|
->ignore($this->id)], |
||||
|
'order' => 'nullable|numeric|min:0', |
||||
|
'private' => 'nullable|boolean', |
||||
|
'color' => 'nullable|string|min:2|max:255', |
||||
|
'active' => 'nullable|boolean', |
||||
|
'description' => 'nullable|string|min:2|max:1000', |
||||
|
// 'members' => empty($this->id) ? '' : 'required|array'
|
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
protected $casts = [ |
||||
|
'private' => 'boolean', |
||||
|
'start' => 'date', |
||||
|
'finish' => 'date', |
||||
|
'has_avatar' => 'boolean', |
||||
|
]; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => $this->id, |
||||
|
'sprint_id' => null, |
||||
|
'workflow_id' => null, |
||||
|
'status_id' => null, |
||||
|
'system_id' => null, |
||||
|
'actor_id' => auth()->id(), |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
public function members() |
||||
|
{ |
||||
|
$permissions = self::$permissions; |
||||
|
return $this->belongsToMany( |
||||
|
User::class, 'project_user', 'project_id', 'user_id', |
||||
|
'id', 'id', __FUNCTION__ |
||||
|
)->using(ReportableRelation::class) |
||||
|
->withPivot($permissions); |
||||
|
} |
||||
|
|
||||
|
public function tasks() |
||||
|
{ |
||||
|
return $this->hasMany(Task::class, 'project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function systems() |
||||
|
{ |
||||
|
return $this->hasMany(System::class, 'project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function sprints() |
||||
|
{ |
||||
|
return $this->hasMany(Sprint::class, 'project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function files() |
||||
|
{ |
||||
|
return $this->hasMany(File::class, 'user_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
// members
|
||||
|
// if (!empty($this->filled_relations['members']) || $this->detach_relation) {
|
||||
|
// $this->dirties['members'] = $this->members()->sync($this->filled_relations['members'], $this->detach_relation);
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
public function reportActivity() |
||||
|
{ |
||||
|
// foreach ($this->dirties as $name => $value) {
|
||||
|
// return \post('task', 'task/v1/log', [
|
||||
|
// 'user_id' => Auth::id(),
|
||||
|
// 'business_id' => $this->business_id,
|
||||
|
// 'loggable_id' => $this->id,
|
||||
|
// 'loggable_type' => $this->getTypeId(),
|
||||
|
// 'action' => $this->getAction(), // id of the action
|
||||
|
// 'data' => [$name => [
|
||||
|
// 'original' => $value['original'],
|
||||
|
// 'diff' => $value['diff'],
|
||||
|
// ]],
|
||||
|
// ]);
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
public function getPermissions($user_id) |
||||
|
{ |
||||
|
return $this->members->where('id',$user_id)->first(); |
||||
|
} |
||||
|
|
||||
|
public function registerMediaCollections(): void |
||||
|
{ |
||||
|
$this->addMediaCollection(static::COLLECTION_NAME) |
||||
|
->acceptsMimeTypes([ |
||||
|
'image/jpeg', |
||||
|
'image/png', |
||||
|
'image/tiff', |
||||
|
'image/gif', |
||||
|
]) |
||||
|
->useDisk('public') |
||||
|
->singleFile(); |
||||
|
} |
||||
|
|
||||
|
public function registerMediaConversions(Media $media = null): void |
||||
|
{ |
||||
|
$this->addMediaConversion(static::CONVERSION_NAME) |
||||
|
->width(200) |
||||
|
->height(200) |
||||
|
->queued() |
||||
|
->nonOptimized() |
||||
|
->performOnCollections(static::COLLECTION_NAME); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function saveAsAvatar(UploadedFile $avatar): void |
||||
|
{ |
||||
|
$this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME); |
||||
|
$this->update([ |
||||
|
'has_avatar' => true, |
||||
|
]); |
||||
|
@unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath()); |
||||
|
} |
||||
|
|
||||
|
public function deleteAvatar(): void |
||||
|
{ |
||||
|
$path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath(); |
||||
|
$this->getFirstMedia(static::COLLECTION_NAME)->delete(); |
||||
|
$this->update([ |
||||
|
'has_avatar' => false, |
||||
|
]); |
||||
|
@unlink($path); |
||||
|
} |
||||
|
|
||||
|
public function getAvatarUrl(): ?string |
||||
|
{ |
||||
|
if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) { |
||||
|
return $url; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use Illuminate\Database\Eloquent\SoftDeletes as SoftDeletesOriginal; |
||||
|
|
||||
|
trait SoftDeletes |
||||
|
{ |
||||
|
use SoftDeletesOriginal; |
||||
|
|
||||
|
protected function performDeleteOnModel() |
||||
|
{ |
||||
|
if ($this->forceDeleting) { |
||||
|
$this->exists = false; |
||||
|
|
||||
|
return $this->setKeysForSaveQuery($this->newModelQuery())->forceDelete(); |
||||
|
} |
||||
|
|
||||
|
$time = $this->freshTimestamp(); |
||||
|
|
||||
|
$this->{$this->getDeletedAtColumn()} = $time; |
||||
|
|
||||
|
if ($this->timestamps && !is_null($this->getUpdatedAtColumn())) { |
||||
|
$this->{$this->getUpdatedAtColumn()} = $time; |
||||
|
} |
||||
|
|
||||
|
return $this->save(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
use Illuminate\Database\Eloquent\SoftDeletes; |
||||
|
|
||||
|
class Sprint extends Model |
||||
|
{ |
||||
|
protected $table = 'sprints'; |
||||
|
|
||||
|
protected $fillable = ['business_id', 'project_id', 'name', 'description', 'started_at', 'ended_at', 'active']; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name', 'started_at', 'ended_at', // fields
|
||||
|
]; |
||||
|
|
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'bail|required|string|min:2|max:225', |
||||
|
'started_at' => 'bail|required|date|date_format:Y-m-d', |
||||
|
'ended_at' => 'bail|required|date|date_format:Y-m-d|after:started_at', |
||||
|
'active' => 'nullable|boolean', |
||||
|
'description' => 'nullable|string|min:2|max:1000', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
protected $casts = [ |
||||
|
'active' => 'boolean' |
||||
|
]; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => $this->project_id, |
||||
|
'sprint_id' => $this->id, |
||||
|
'workflow_id' => null, |
||||
|
'status_id' => null, |
||||
|
'system_id' => null, |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function projects() |
||||
|
{ |
||||
|
return $this->belongsTo(Project::class, 'project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function tasks() |
||||
|
{ |
||||
|
return $this->hasMany(Task::class, 'sprint_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
|
||||
|
class Status extends Model |
||||
|
{ |
||||
|
protected $table = 'statuses'; |
||||
|
|
||||
|
protected $fillable = [ |
||||
|
'business_id', 'workflow_id', 'name', 'state', 'order' |
||||
|
]; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name', 'state', 'order' |
||||
|
]; |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' =>'required|string|min:3|max:255', |
||||
|
'state' => 'nullable|between:0,3', |
||||
|
'order' => 'nullable|numeric|min:0' |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'workflow_id' => $this->workflow_id, |
||||
|
'status_id' => $this->id, |
||||
|
'system_id' => null, |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function workflow() |
||||
|
{ |
||||
|
return $this->belongsTo(Workflow::class,'workflow_id','id'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Database\Eloquent\SoftDeletes; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
|
||||
|
class System extends Model |
||||
|
{ |
||||
|
protected $table = 'systems'; |
||||
|
|
||||
|
protected $fillable = ['business_id', 'project_id', 'name']; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name' |
||||
|
]; |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'required|string|min:2|max:225', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
protected $casts = [ |
||||
|
'private' => 'boolean' |
||||
|
]; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => $this->project_id, |
||||
|
'sprint_id' => null, |
||||
|
'workflow_id' => null, |
||||
|
'status_id' => null, |
||||
|
'system_id' => $this->id, |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function project() |
||||
|
{ |
||||
|
return $this->belongsTo(Project::class, 'project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function tasks() |
||||
|
{ |
||||
|
return $this->hasMany(Task::class, 'sub_project_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use App\Scopes\BusinessScope; |
||||
|
|
||||
|
class Tag extends Model |
||||
|
{ |
||||
|
protected $fillable = ['business_id', 'label', 'color']; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'label', 'color' |
||||
|
]; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'workflow_id' => null, |
||||
|
'status_id' => null, |
||||
|
'system_id' => null, |
||||
|
'user_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'label' => 'required|string|min:3|max:225', |
||||
|
'color' => 'nullable|string|min:2|max:255', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class,'business_id','id',__FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function task() |
||||
|
{ |
||||
|
return $this->belongsToMany( |
||||
|
Task::class,'tag_task','tag_id','task_id', |
||||
|
'id','id',__FUNCTION__ |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\RemoteModel; |
||||
|
|
||||
|
class Task extends RemoteModel |
||||
|
{ |
||||
|
public string $host; |
||||
|
public ?string $path; |
||||
|
|
||||
|
public function __construct(array $attributes = []) |
||||
|
{ |
||||
|
parent::__construct($attributes); |
||||
|
|
||||
|
$this->host = 'hi-task-app'; |
||||
|
$this->path = 'task/v1/businesses/'.request('_business_info')['id'].'/tasks/'; |
||||
|
} |
||||
|
|
||||
|
public function files() |
||||
|
{ |
||||
|
return $this->hasMany(File::class, 'attached_to_id', 'id') |
||||
|
->where('attached_to_table', enum('tables.tasks.id')); |
||||
|
} |
||||
|
} |
@ -0,0 +1,164 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use App\Utilities\Zarinpal\Laravel\Facade\Zarinpal; |
||||
|
use Illuminate\Support\Arr; |
||||
|
|
||||
|
class Transaction extends Model |
||||
|
{ |
||||
|
protected $table = 'transactions'; |
||||
|
|
||||
|
protected $fillable = [ |
||||
|
'user_id', 'business_id', 'amount', 'succeeded', 'options' |
||||
|
]; |
||||
|
|
||||
|
|
||||
|
protected $casts = [ |
||||
|
'options' => 'array', |
||||
|
'succeeded' => 'boolean', |
||||
|
]; |
||||
|
|
||||
|
protected $fillable_relations = [ |
||||
|
'user', 'business' |
||||
|
]; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'user_id', 'business_id', 'amount', 'succeeded', 'options', // fields
|
||||
|
]; |
||||
|
|
||||
|
public $perPage = 12; |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'system_id' => null, |
||||
|
'user_id' => $this->user_id, |
||||
|
'workflow_id' => null, |
||||
|
'status_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function rules(): array |
||||
|
{ |
||||
|
return [ |
||||
|
'user_id' => 'required|', |
||||
|
'business_id' => 'required', |
||||
|
'amount' => 'required|integer|min:1', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
// user relations
|
||||
|
if (!empty($this->filled_relations['user'])) { |
||||
|
$this->dirties['user'] = $this->user_id; |
||||
|
} |
||||
|
|
||||
|
// business relations
|
||||
|
if (!empty($this->filled_relations['business'])) { |
||||
|
$this->dirties['business'] = $this->business_id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function reportActivity() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public function user() |
||||
|
{ |
||||
|
return $this->belongsTo(User::class, 'user_id','id',__FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id','id',__FUNCTION__); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Receive the authority key from the payment gateway |
||||
|
*/ |
||||
|
public function prepare(): Transaction |
||||
|
{ |
||||
|
$results = Zarinpal::request( |
||||
|
config('services.zarinpal.callback-url'), |
||||
|
$this->amount, |
||||
|
config('services.zarinpal.description') |
||||
|
); |
||||
|
|
||||
|
$this->options = $results; |
||||
|
$this->save(); |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Redirect to the payment gateway |
||||
|
*/ |
||||
|
public function redirect() |
||||
|
{ |
||||
|
return Zarinpal::redirect(); |
||||
|
} |
||||
|
|
||||
|
public function verify(): Transaction |
||||
|
{ |
||||
|
$results = Zarinpal::verify($this->amount, $this->options['Authority']); |
||||
|
if ($results['Status'] == 'verified_before') { |
||||
|
throw new \Exception("تراکنش قبلا تایید شده است."); |
||||
|
} |
||||
|
|
||||
|
if ($results['Status'] == 'success') { |
||||
|
$this->succeeded = true; |
||||
|
} else { |
||||
|
$this->succeeded = false; |
||||
|
} |
||||
|
|
||||
|
$options = array_merge($this->options, $results); |
||||
|
$this->options = $options; |
||||
|
$this->save(); |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Find a transaction via the authoriry key that psp provides us |
||||
|
* |
||||
|
* @throw ModelNotFound |
||||
|
*/ |
||||
|
public static function findByAuthority(string $authority): Transaction |
||||
|
{ |
||||
|
return static::where('options->Authority','=',$authority)->firstOrFail(); |
||||
|
} |
||||
|
|
||||
|
public function isWentToPaymentGateway(): bool |
||||
|
{ |
||||
|
return !empty($this->options); |
||||
|
} |
||||
|
|
||||
|
public function hasBeenAppliedToWallet(): bool |
||||
|
{ |
||||
|
return Arr::get($this->options,"applied", false); |
||||
|
} |
||||
|
|
||||
|
public function amountWasAppliedToWallet() |
||||
|
{ |
||||
|
$options = $this->options; |
||||
|
$options['applied'] = true; |
||||
|
$this->options = $options; |
||||
|
|
||||
|
$this->save(); |
||||
|
} |
||||
|
} |
@ -1,43 +1,184 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
namespace App\Models; |
|
||||
|
namespace App; |
||||
|
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail; |
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory; |
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable; |
|
||||
use Illuminate\Notifications\Notifiable; |
|
||||
|
use App\File; |
||||
|
use App\SoftDeletes; |
||||
|
use App\HiLib\Models\Model; |
||||
|
use Illuminate\Validation\Rule; |
||||
|
use Illuminate\Http\UploadedFile; |
||||
|
use Spatie\MediaLibrary\HasMedia; |
||||
|
use Illuminate\Auth\Authenticatable; |
||||
|
use Laravel\Lumen\Auth\Authorizable; |
||||
|
use App\HiLib\Models\ReportableRelation; |
||||
|
use Spatie\MediaLibrary\InteractsWithMedia; |
||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media; |
||||
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; |
||||
|
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; |
||||
|
|
||||
class User extends Authenticatable |
|
||||
|
class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia |
||||
{ |
{ |
||||
use HasFactory, Notifiable; |
|
||||
|
|
||||
/** |
|
||||
* The attributes that are mass assignable. |
|
||||
* |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $fillable = [ |
|
||||
'name', |
|
||||
'email', |
|
||||
'password', |
|
||||
]; |
|
||||
|
use SoftDeletes, |
||||
|
Authorizable, |
||||
|
Authenticatable, |
||||
|
InteractsWithMedia; |
||||
|
|
||||
|
const CONVERSION_NAME = 'avatar'; |
||||
|
|
||||
/** |
|
||||
* The attributes that should be hidden for arrays. |
|
||||
* |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $hidden = [ |
|
||||
'password', |
|
||||
'remember_token', |
|
||||
|
const COLLECTION_NAME = 'avatars'; |
||||
|
|
||||
|
public $casts = [ |
||||
|
'has_avatar' => 'boolean', |
||||
]; |
]; |
||||
|
|
||||
/** |
|
||||
* The attributes that should be cast to native types. |
|
||||
* |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $casts = [ |
|
||||
'email_verified_at' => 'datetime', |
|
||||
|
protected $fillable = ['name', 'email','mobile', 'username','password','active','has_avatar']; |
||||
|
|
||||
|
protected $fillable_relations = ['projects']; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name', 'username', 'mobile', 'email', // fields
|
||||
|
['projects' => 'project_user'] |
||||
]; |
]; |
||||
|
|
||||
|
public $detach_relation = false; |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
// projects relations
|
||||
|
if (!empty($this->filled_relations['projects']) || $this->detach_relation) { |
||||
|
$this->dirties['projects'] = $this->projects()->sync($this->filled_relations['projects'], $this->detach_relation); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** =============================== Validations ======================== */ |
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'required|string|max:225|min:2', |
||||
|
'username' => ['required', Rule::unique('users', 'username')->ignore($this->id)], |
||||
|
'email' => ['required', 'email', Rule::unique('users', 'email')->ignore($this->id)], |
||||
|
'password' => ['required','string','min:8'] |
||||
|
]; |
||||
|
} |
||||
|
/** =============================== End Validations ==================== */ |
||||
|
|
||||
|
|
||||
|
/** =============================== Relations ========================== */ |
||||
|
public function fingerprints() |
||||
|
{ |
||||
|
return $this->hasMany(Fingerprint::class, 'user_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function businesses() |
||||
|
{ |
||||
|
return $this->belongsToMany( |
||||
|
Business::class, |
||||
|
'business_user', |
||||
|
'user_id', |
||||
|
'business_id', |
||||
|
'id', |
||||
|
'id', |
||||
|
__FUNCTION__ |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function tasks() |
||||
|
{ |
||||
|
return $this->hasMany(Task::class, 'user_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function projects() |
||||
|
{ |
||||
|
return $this->belongsToMany( |
||||
|
Project::class, |
||||
|
'project_user', |
||||
|
'user_id', |
||||
|
'project_id', |
||||
|
'id', |
||||
|
'id', |
||||
|
__FUNCTION__ |
||||
|
)->using(ReportableRelation::class); |
||||
|
} |
||||
|
|
||||
|
public function files() |
||||
|
{ |
||||
|
return $this->hasMany(File::class, 'user_id','id'); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** =============================== End Relations ====================== */ |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => request('_business_info')['id'] ?? null, |
||||
|
'user_id' => $this->id, |
||||
|
'workflow_id' => null, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'system_id' => null, |
||||
|
'status_id' => null, |
||||
|
'task_id' => null, |
||||
|
'subject_id' => $this->id, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function registerMediaCollections(): void |
||||
|
{ |
||||
|
$this->addMediaCollection(static::COLLECTION_NAME) |
||||
|
->acceptsMimeTypes([ |
||||
|
'image/jpeg', |
||||
|
'image/png', |
||||
|
'image/tiff', |
||||
|
'image/gif', |
||||
|
]) |
||||
|
->useDisk('public') |
||||
|
->singleFile(); |
||||
|
} |
||||
|
|
||||
|
public function registerMediaConversions(Media $media = null): void |
||||
|
{ |
||||
|
$this->addMediaConversion(static::CONVERSION_NAME) |
||||
|
->width(200) |
||||
|
->height(200) |
||||
|
->queued() |
||||
|
->nonOptimized() |
||||
|
->performOnCollections(static::COLLECTION_NAME); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function saveAsAvatar(UploadedFile $avatar): void |
||||
|
{ |
||||
|
$this->addMedia($avatar)->toMediaCollection(static::COLLECTION_NAME); |
||||
|
$this->update([ |
||||
|
'has_avatar' => true, |
||||
|
]); |
||||
|
@unlink($this->getFirstMedia(static::COLLECTION_NAME)->getPath()); |
||||
|
} |
||||
|
|
||||
|
public function deleteAvatar(): void |
||||
|
{ |
||||
|
$path = $this->getFirstMedia(static::COLLECTION_NAME)->getPath(); |
||||
|
$this->getFirstMedia(static::COLLECTION_NAME)->delete(); |
||||
|
$this->update([ |
||||
|
'has_avatar' => false, |
||||
|
]); |
||||
|
@unlink($path); |
||||
|
} |
||||
|
|
||||
|
public function getAvatarUrl(): ?string |
||||
|
{ |
||||
|
if ($url = $this->getFirstMediaUrl(static::COLLECTION_NAME, static::CONVERSION_NAME)) { |
||||
|
return $url; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
} |
} |
@ -0,0 +1,88 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App; |
||||
|
|
||||
|
use App\HiLib\Models\Model; |
||||
|
use App\Scopes\BusinessScope; |
||||
|
|
||||
|
class Workflow extends Model |
||||
|
{ |
||||
|
protected $fillable = ['business_id', 'name', 'desc', 'statuses']; |
||||
|
|
||||
|
protected $reportable = [ |
||||
|
'name' |
||||
|
]; |
||||
|
|
||||
|
protected $fillable_relations = ['statuses']; |
||||
|
|
||||
|
public function rules() |
||||
|
{ |
||||
|
return [ |
||||
|
'name' => 'required|string|min:3|max:225', |
||||
|
'desc' => 'nullable|string|min:3|max:225', |
||||
|
'statuses' => 'required|array|min:2', |
||||
|
'statuses.*' => 'required|array|min:2', |
||||
|
'statuses.*.id' => 'nullable|numeric', |
||||
|
'statuses.*.name' => 'required|string|min:3', |
||||
|
'statuses.*.state' => 'required|numeric|between:0,3', |
||||
|
'statuses.*.order' => 'nullable|numeric', |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
public function getValueOf(?string $key) |
||||
|
{ |
||||
|
$values = [ |
||||
|
'business_id' => $this->business_id, |
||||
|
'project_id' => null, |
||||
|
'sprint_id' => null, |
||||
|
'workflow_id' => $this->id, |
||||
|
'status_id' => null, |
||||
|
'system_id' => null, |
||||
|
'user_id' => null, |
||||
|
]; |
||||
|
|
||||
|
if ($key && isset($values, $key)) { |
||||
|
return $values[$key]; |
||||
|
} |
||||
|
|
||||
|
return $values; |
||||
|
} |
||||
|
|
||||
|
public function updateRelations() |
||||
|
{ |
||||
|
$old_statuses_name = isset(\request('_business_info')['workflows'][$this->id]['statuses']) ? |
||||
|
array_keys(collect(\request('_business_info')['workflows'][$this->id]['statuses'])->toArray()) : |
||||
|
[]; |
||||
|
$new_statuses_name = array_keys(collect($this->filled_relations['statuses'])->keyBy('id')->toArray()); |
||||
|
$removed_statuses_name = array_diff(array_merge($old_statuses_name, $new_statuses_name), $new_statuses_name); |
||||
|
|
||||
|
foreach ($removed_statuses_name as $status_name) { |
||||
|
//delete all statuses that removed name's from request->statuses
|
||||
|
$this->statuses()->where('id', $status_name)->first()->delete(); |
||||
|
} |
||||
|
|
||||
|
foreach (request('statuses') as $status) { |
||||
|
//sync another statuses
|
||||
|
$this->statuses() |
||||
|
->updateOrCreate( |
||||
|
['id' => $status['id'] ?? null, 'business_id' => $this->business_id, 'workflow_id' => $this->id], |
||||
|
['name' => $status['name'], 'state' => $status['state'], 'order' => $status['order']] |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function business() |
||||
|
{ |
||||
|
return $this->belongsTo(Business::class, 'business_id'); |
||||
|
} |
||||
|
|
||||
|
public function statuses() |
||||
|
{ |
||||
|
return $this->hasMany(Status::class, 'workflow_id', 'id'); |
||||
|
} |
||||
|
|
||||
|
public function tasks() |
||||
|
{ |
||||
|
return $this->hasMany(Task::class, 'workflow_id', 'id'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Rules; |
||||
|
|
||||
|
use Illuminate\Contracts\Validation\Rule; |
||||
|
|
||||
|
class MaxBound implements Rule |
||||
|
{ |
||||
|
/** |
||||
|
* Create a new rule instance. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function __construct() |
||||
|
{ |
||||
|
//
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Determine if the validation rule passes. |
||||
|
* |
||||
|
* @param string $attribute |
||||
|
* @param mixed $value |
||||
|
* @return bool |
||||
|
*/ |
||||
|
public function passes($attribute, $value) |
||||
|
{ |
||||
|
return sizeof(explode(',', trim($value, ','))) <= $this->bound; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the validation error message. |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function message() |
||||
|
{ |
||||
|
return 'The :attribute is out of bound.'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
<?php |
||||
|
|
||||
|
|
||||
|
namespace App\Scopes; |
||||
|
|
||||
|
|
||||
|
use Illuminate\Database\Eloquent\Builder; |
||||
|
use Illuminate\Database\Eloquent\Model; |
||||
|
use Illuminate\Database\Eloquent\Scope; |
||||
|
|
||||
|
class BusinessScope implements Scope |
||||
|
{ |
||||
|
|
||||
|
public function apply(Builder $builder, Model $model) |
||||
|
{ |
||||
|
$builder->where('business_id', '=', request('business_id')); |
||||
|
} |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Avatar; |
||||
|
|
||||
|
use Spatie\MediaLibrary\Conversions\Conversion; |
||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media; |
||||
|
use Spatie\MediaLibrary\Conversions\ConversionFileNamer; |
||||
|
|
||||
|
class DefaultConversionFileNamer extends ConversionFileNamer |
||||
|
{ |
||||
|
public function getFileName(Conversion $conversion, Media $media): string |
||||
|
{ |
||||
|
return $media->model->getKey(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Avatar; |
||||
|
|
||||
|
use Spatie\MediaLibrary\MediaCollections\Models\Media; |
||||
|
use Spatie\MediaLibrary\Support\PathGenerator\PathGenerator; |
||||
|
|
||||
|
class DefaultPathGenerator implements PathGenerator |
||||
|
{ |
||||
|
/* |
||||
|
* Get a unique base path for the given media. |
||||
|
*/ |
||||
|
protected function getBasePath(Media $media): string |
||||
|
{ |
||||
|
return $media->model->getTable()."/"; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Get the path for the given media, relative to the root storage path. |
||||
|
*/ |
||||
|
public function getPath(Media $media): string |
||||
|
{ |
||||
|
return $this->getBasePath($media); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Get the path for conversions of the given media, relative to the root storage path. |
||||
|
*/ |
||||
|
public function getPathForConversions(Media $media): string |
||||
|
{ |
||||
|
return $this->getBasePath($media); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Get the path for responsive images of the given media, relative to the root storage path. |
||||
|
*/ |
||||
|
public function getPathForResponsiveImages(Media $media): string |
||||
|
{ |
||||
|
return $this->getBasePath($media).'/responsive-images/'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities; |
||||
|
|
||||
|
use App\User; |
||||
|
use Illuminate\Support\Arr; |
||||
|
use Jenssegers\Agent\Agent; |
||||
|
|
||||
|
/** |
||||
|
* @mixin Request |
||||
|
* Class RequestMixin |
||||
|
*/ |
||||
|
class BusinessInfoRequestMixin |
||||
|
{ |
||||
|
public function getBusinessInfo() |
||||
|
{ |
||||
|
return function () { |
||||
|
return Arr::get($this, '_business_info'); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public function getFromBusinessInfo() |
||||
|
{ |
||||
|
return function(string $key) { |
||||
|
return Arr::get($this, "_business_info.$key"); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public function getBusinessUsers() |
||||
|
{ |
||||
|
return function() { |
||||
|
return $this->getFromBusinessInfo('info.users'); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public function isBusinessOwner() |
||||
|
{ |
||||
|
return function(User $user) { |
||||
|
return Arr::get($this, "_business_info.info.users.{$user->id}.level") != enum('levels.owner.id'); |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities; |
||||
|
|
||||
|
use ArrayAccess; |
||||
|
use App\Models\Notifiable; |
||||
|
use Illuminate\Support\Arr; |
||||
|
use Illuminate\Support\Str; |
||||
|
use Illuminate\Support\Collection; |
||||
|
use Illuminate\Support\Stringable; |
||||
|
|
||||
|
class Payload implements ArrayAccess |
||||
|
{ |
||||
|
public array $payload; |
||||
|
|
||||
|
public function __construct(array $payload) |
||||
|
{ |
||||
|
$this->payload = $payload; |
||||
|
} |
||||
|
|
||||
|
public function getActor(): ?Notifiable |
||||
|
{ |
||||
|
return new Notifiable(Arr::get($this->payload, "info.users." . $this['auth'])); |
||||
|
} |
||||
|
|
||||
|
public function getSubject(): ?Notifiable |
||||
|
{ |
||||
|
return new Notifiable( |
||||
|
Arr::get($this->payload, "info.users." . $this['data']['original']['user_id']) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public function getOwners(): Collection |
||||
|
{ |
||||
|
return new Collection( |
||||
|
Arr::where($this->payload['info']['users'], fn ($user) => $user['level'] === 4) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public function getTableName(): ?Stringable |
||||
|
{ |
||||
|
return Str::of(Arr::get($this->payload, "data.table_name")); |
||||
|
} |
||||
|
|
||||
|
public function getActId() |
||||
|
{ |
||||
|
return Arr::get($this->payload, "data.crud_id"); |
||||
|
} |
||||
|
|
||||
|
public function getTitle() |
||||
|
{ |
||||
|
return Arr::get($this->payload, "info.name"); |
||||
|
} |
||||
|
|
||||
|
public function isPivot(): bool |
||||
|
{ |
||||
|
return $this->getTableName()->contains("_"); |
||||
|
} |
||||
|
|
||||
|
public function offsetExists($key) |
||||
|
{ |
||||
|
return isset($this->payload[$key]); |
||||
|
} |
||||
|
|
||||
|
public function offsetGet($key) |
||||
|
{ |
||||
|
return $this->payload[$key]; |
||||
|
} |
||||
|
|
||||
|
public function offsetSet($key, $value) |
||||
|
{ |
||||
|
if (is_null($key)) { |
||||
|
$this->payload[] = $value; |
||||
|
} else { |
||||
|
$this->payload[$key] = $value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public function offsetUnset($key) |
||||
|
{ |
||||
|
unset($this->payload[$key]); |
||||
|
} |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities; |
||||
|
|
||||
|
use Closure; |
||||
|
use Jenssegers\Agent\Agent; |
||||
|
use Illuminate\Support\Facades\Auth; |
||||
|
|
||||
|
/** |
||||
|
* @mixin Request |
||||
|
* Class RequestMixin |
||||
|
*/ |
||||
|
class RequestMixin |
||||
|
{ |
||||
|
/** |
||||
|
* Return the OS |
||||
|
* |
||||
|
* @return Closure |
||||
|
*/ |
||||
|
public function getOS() |
||||
|
{ |
||||
|
return fn() => $this->hasHeader('USER_AGENT') ? (new Agent())->platform() : null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Return the browser |
||||
|
* |
||||
|
* @return Closure |
||||
|
*/ |
||||
|
public function getAgent() |
||||
|
{ |
||||
|
return fn() => $this->hasHeader('USER_AGENT') ? (new Agent())->browser() : null; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Get the user location's based on her/his IP address |
||||
|
* |
||||
|
* @return Closure |
||||
|
*/ |
||||
|
public function getLocation() |
||||
|
{ |
||||
|
return fn() => geoip()->getLocation($this->getClientIp()); |
||||
|
} |
||||
|
|
||||
|
public function getCurrentToken() |
||||
|
{ |
||||
|
// todo: how to implement ip lookup for current token
|
||||
|
return fn() => Auth::user()->token; |
||||
|
} |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Zarinpal\Drivers; |
||||
|
|
||||
|
interface DriverInterface |
||||
|
{ |
||||
|
/** |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array|redirect |
||||
|
*/ |
||||
|
public function request($inputs); |
||||
|
|
||||
|
/** |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array|redirect |
||||
|
*/ |
||||
|
public function requestWithExtra($inputs); |
||||
|
|
||||
|
/** |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function verify($inputs); |
||||
|
|
||||
|
/** |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function verifyWithExtra($inputs); |
||||
|
|
||||
|
/** |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function setAddress($inputs); |
||||
|
|
||||
|
/** |
||||
|
* activate sandbox mod for dev environment. |
||||
|
*/ |
||||
|
public function enableSandbox(); |
||||
|
} |
@ -0,0 +1,197 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Zarinpal\Drivers; |
||||
|
|
||||
|
use GuzzleHttp\Client; |
||||
|
use GuzzleHttp\Exception\RequestException; |
||||
|
|
||||
|
class RestDriver implements DriverInterface |
||||
|
{ |
||||
|
protected $baseUrl = 'https://www.zarinpal.com/pg/rest/WebGate/'; |
||||
|
|
||||
|
/** |
||||
|
* request driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function request($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('PaymentRequest.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return ['Authority' => $result['Authority']]; |
||||
|
} else { |
||||
|
return ['error' => $result['Status']]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* requestWithExtra driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function requestWithExtra($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('PaymentRequestWithExtra.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return ['Authority' => $result['Authority']]; |
||||
|
} else { |
||||
|
return [ |
||||
|
'Status' => 'error', |
||||
|
'error' => !empty($result['Status']) ? $result['Status'] : null, |
||||
|
'errorInfo' => !empty($result['errors']) ? $result['errors'] : null, |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* verify driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function verify($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('PaymentVerification.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return [ |
||||
|
'Status' => 'success', |
||||
|
'RefID' => $result['RefID'], |
||||
|
]; |
||||
|
} elseif ($result['Status'] == 101) { |
||||
|
return [ |
||||
|
'Status' => 'verified_before', |
||||
|
'RefID' => $result['RefID'], |
||||
|
]; |
||||
|
} else { |
||||
|
return [ |
||||
|
'Status' => 'error', |
||||
|
'error' => !empty($result['Status']) ? $result['Status'] : null, |
||||
|
'errorInfo' => !empty($result['errors']) ? $result['errors'] : null, |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* verifyWithExtra driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function verifyWithExtra($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('PaymentVerificationWithExtra.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return [ |
||||
|
'Status' => 'success', |
||||
|
'RefID' => $result['RefID'], |
||||
|
'ExtraDetail' => $result['ExtraDetail'], |
||||
|
]; |
||||
|
} elseif ($result['Status'] == 101) { |
||||
|
return [ |
||||
|
'Status' => 'verified_before', |
||||
|
'RefID' => $result['RefID'], |
||||
|
'ExtraDetail' => $result['ExtraDetail'], |
||||
|
]; |
||||
|
} else { |
||||
|
return [ |
||||
|
'Status' => 'error', |
||||
|
'error' => !empty($result['Status']) ? $result['Status'] : null, |
||||
|
'errorInfo' => !empty($result['errors']) ? $result['errors'] : null, |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* unverifiedTransactions driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function unverifiedTransactions($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('UnverifiedTransactions.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return ['Status' => 'success', 'Authorities' => $result['Authorities']]; |
||||
|
} else { |
||||
|
return [ |
||||
|
'Status' => 'error', |
||||
|
'error' => !empty($result['Status']) ? $result['Status'] : null, |
||||
|
'errorInfo' => !empty($result['errors']) ? $result['errors'] : null, |
||||
|
]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* refreshAuthority driver. |
||||
|
* |
||||
|
* @param $inputs |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function refreshAuthority($inputs) |
||||
|
{ |
||||
|
$result = $this->restCall('RefreshAuthority.json', $inputs); |
||||
|
|
||||
|
if ($result['Status'] == 100) { |
||||
|
return ['Status' => 'success', 'refreshed' => true]; |
||||
|
} else { |
||||
|
return ['Status' => 'error', 'error' => $result['Status']]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* request rest and return the response. |
||||
|
* |
||||
|
* @param $uri |
||||
|
* @param $data |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
private function restCall($uri, $data) |
||||
|
{ |
||||
|
try { |
||||
|
$client = new Client(['base_uri' => $this->baseUrl]); |
||||
|
$response = $client->request('POST', $uri, ['json' => $data]); |
||||
|
|
||||
|
$rawBody = $response->getBody()->getContents(); |
||||
|
$body = json_decode($rawBody, true); |
||||
|
} catch (RequestException $e) { |
||||
|
$response = $e->getResponse(); |
||||
|
$rawBody = is_null($response) ? '{"Status":-98,"message":"http connection error"}' : $response->getBody()->getContents(); |
||||
|
$body = json_decode($rawBody, true); |
||||
|
} |
||||
|
|
||||
|
if (!isset($result['Status'])) { |
||||
|
$result['Status'] = -99; |
||||
|
} |
||||
|
|
||||
|
return $body; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param mixed $baseUrl |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function setAddress($baseUrl) |
||||
|
{ |
||||
|
$this->baseUrl = $baseUrl; |
||||
|
} |
||||
|
|
||||
|
public function enableSandbox() |
||||
|
{ |
||||
|
$this->setAddress('https://sandbox.zarinpal.com/pg/rest/WebGate/'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Zarinpal\Laravel\Facade; |
||||
|
|
||||
|
use Illuminate\Support\Facades\Facade; |
||||
|
|
||||
|
/** |
||||
|
* @method static request($callbackURL, $Amount, $Description, $Email = null, $Mobile = null,$additionalData = null) |
||||
|
* @method static verify($status, $amount, $authority) |
||||
|
* @method static redirect() |
||||
|
* @method static getDriver() |
||||
|
*/ |
||||
|
class Zarinpal extends Facade |
||||
|
{ |
||||
|
/** |
||||
|
* Get the registered name of the component. |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
protected static function getFacadeAccessor() |
||||
|
{ |
||||
|
return 'Zarinpal'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Zarinpal\Laravel; |
||||
|
|
||||
|
use App\Utilities\Zarinpal\Zarinpal; |
||||
|
use Illuminate\Support\ServiceProvider; |
||||
|
use App\Utilities\Zarinpal\Drivers\RestDriver; |
||||
|
use App\Utilities\Zarinpal\Drivers\DriverInterface; |
||||
|
|
||||
|
class ZarinpalServiceProvider extends ServiceProvider |
||||
|
{ |
||||
|
/** |
||||
|
* Register the service provider. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function register() |
||||
|
{ |
||||
|
$this->app->singleton(DriverInterface::class, function () { |
||||
|
return new RestDriver(); |
||||
|
}); |
||||
|
|
||||
|
$this->app->singleton('Zarinpal', function () { |
||||
|
$merchantID = config('services.zarinpal.merchantID', config('Zarinpal.merchantID', 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX')); |
||||
|
|
||||
|
$zarinpal = new Zarinpal($merchantID, $this->app->make(DriverInterface::class)); |
||||
|
|
||||
|
if (config('services.zarinpal.sandbox', false)) { |
||||
|
$zarinpal->enableSandbox(); |
||||
|
} |
||||
|
if (config('services.zarinpal.zarinGate', false)) { |
||||
|
$zarinpal->isZarinGate(); |
||||
|
} |
||||
|
|
||||
|
return $zarinpal; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Publish the plugin configuration. |
||||
|
*/ |
||||
|
public function boot() |
||||
|
{ |
||||
|
//
|
||||
|
} |
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace App\Utilities\Zarinpal; |
||||
|
|
||||
|
use App\Utilities\Zarinpal\Drivers\RestDriver; |
||||
|
use App\Utilities\Zarinpal\Drivers\DriverInterface; |
||||
|
|
||||
|
class Zarinpal |
||||
|
{ |
||||
|
private $redirectUrl = 'https://www.zarinpal.com/pg/StartPay/%u'; |
||||
|
private $merchantID; |
||||
|
private $driver; |
||||
|
private $Authority; |
||||
|
|
||||
|
public function __construct($merchantID, DriverInterface $driver = null) |
||||
|
{ |
||||
|
if (is_null($driver)) { |
||||
|
$driver = new RestDriver(); |
||||
|
} |
||||
|
$this->merchantID = $merchantID; |
||||
|
$this->driver = $driver; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* send request for money to zarinpal |
||||
|
* and redirect if there was no error. |
||||
|
* |
||||
|
* @param string $callbackURL |
||||
|
* @param string $Amount |
||||
|
* @param string $Description |
||||
|
* @param string $Email |
||||
|
* @param string $Mobile |
||||
|
* @param null $additionalData |
||||
|
* |
||||
|
* @return array|@redirect |
||||
|
*/ |
||||
|
public function request($callbackURL, $Amount, $Description, $Email = null, $Mobile = null, $additionalData = null) |
||||
|
{ |
||||
|
$inputs = [ |
||||
|
'MerchantID' => $this->merchantID, |
||||
|
'CallbackURL' => $callbackURL, |
||||
|
'Amount' => $Amount, |
||||
|
'Description' => $Description, |
||||
|
]; |
||||
|
if (!is_null($Email)) { |
||||
|
$inputs['Email'] = $Email; |
||||
|
} |
||||
|
if (!is_null($Mobile)) { |
||||
|
$inputs['Mobile'] = $Mobile; |
||||
|
} |
||||
|
if (!is_null($additionalData)) { |
||||
|
$inputs['AdditionalData'] = $additionalData; |
||||
|
$results = $this->driver->requestWithExtra($inputs); |
||||
|
} else { |
||||
|
$results = $this->driver->request($inputs); |
||||
|
} |
||||
|
|
||||
|
if (empty($results['Authority'])) { |
||||
|
$results['Authority'] = null; |
||||
|
} |
||||
|
$this->Authority = $results['Authority']; |
||||
|
|
||||
|
return $results; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* verify that the bill is paid or not |
||||
|
* by checking authority, amount and status. |
||||
|
* |
||||
|
* @param $amount |
||||
|
* @param $authority |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function verify($amount, $authority) |
||||
|
{ |
||||
|
// backward compatibility
|
||||
|
if (count(func_get_args()) == 3) { |
||||
|
$amount = func_get_arg(1); |
||||
|
$authority = func_get_arg(2); |
||||
|
} |
||||
|
|
||||
|
$inputs = [ |
||||
|
'MerchantID' => $this->merchantID, |
||||
|
'Authority' => $authority, |
||||
|
'Amount' => $amount, |
||||
|
]; |
||||
|
|
||||
|
return $this->driver->verifyWithExtra($inputs); |
||||
|
} |
||||
|
|
||||
|
public function redirect() |
||||
|
{ |
||||
|
header('Location: ' . sprintf($this->redirectUrl, $this->Authority)); |
||||
|
die; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function redirectUrl() |
||||
|
{ |
||||
|
return sprintf($this->redirectUrl, $this->Authority); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return DriverInterface |
||||
|
*/ |
||||
|
public function getDriver() |
||||
|
{ |
||||
|
return $this->driver; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* active sandbox mod for test env. |
||||
|
*/ |
||||
|
public function enableSandbox() |
||||
|
{ |
||||
|
$this->redirectUrl = 'https://sandbox.zarinpal.com/pg/StartPay/%u'; |
||||
|
$this->getDriver()->enableSandbox(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* active zarinGate mode. |
||||
|
*/ |
||||
|
public function isZarinGate() |
||||
|
{ |
||||
|
$this->redirectUrl = $this->redirectUrl . '/ZarinGate'; |
||||
|
} |
||||
|
} |
4094
composer.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,232 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Name |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This value is the name of your application. This value is used when the |
||||
|
| framework needs to place the application's name in a notification or |
||||
|
| any other location as required by the application or its packages. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'name' => env('APP_NAME', 'Laravel'), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Environment |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This value determines the "environment" your application is currently |
||||
|
| running in. This may determine how you prefer to configure various |
||||
|
| services the application utilizes. Set this in your ".env" file. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'env' => env('APP_ENV', 'production'), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Debug Mode |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| When your application is in debug mode, detailed error messages with |
||||
|
| stack traces will be shown on every error that occurs within your |
||||
|
| application. If disabled, a simple generic error page is shown. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'debug' => (bool) env('APP_DEBUG', false), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application URL |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This URL is used by the console to properly generate URLs when using |
||||
|
| the Artisan command line tool. You should set this to the root of |
||||
|
| your application so that it is used when running Artisan tasks. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'url' => env('APP_URL', 'http://localhost'), |
||||
|
|
||||
|
'asset_url' => env('ASSET_URL', null), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Timezone |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may specify the default timezone for your application, which |
||||
|
| will be used by the PHP date and date-time functions. We have gone |
||||
|
| ahead and set this to a sensible default for you out of the box. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'timezone' => 'UTC', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Locale Configuration |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| The application locale determines the default locale that will be used |
||||
|
| by the translation service provider. You are free to set this value |
||||
|
| to any of the locales which will be supported by the application. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'locale' => 'en', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Application Fallback Locale |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| The fallback locale determines the locale to use when the current one |
||||
|
| is not available. You may change the value to correspond to any of |
||||
|
| the language folders that are provided through your application. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'fallback_locale' => 'en', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Faker Locale |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This locale will be used by the Faker PHP library when generating fake |
||||
|
| data for your database seeds. For example, this will be used to get |
||||
|
| localized telephone numbers, street address information and more. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'faker_locale' => 'en_US', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Encryption Key |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This key is used by the Illuminate encrypter service and should be set |
||||
|
| to a random, 32 character string, otherwise these encrypted strings |
||||
|
| will not be safe. Please do this before deploying an application! |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'key' => env('APP_KEY'), |
||||
|
|
||||
|
'cipher' => 'AES-256-CBC', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Autoloaded Service Providers |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| The service providers listed here will be automatically loaded on the |
||||
|
| request to your application. Feel free to add your own services to |
||||
|
| this array to grant expanded functionality to your applications. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'providers' => [ |
||||
|
|
||||
|
/* |
||||
|
* Laravel Framework Service Providers... |
||||
|
*/ |
||||
|
Illuminate\Auth\AuthServiceProvider::class, |
||||
|
Illuminate\Broadcasting\BroadcastServiceProvider::class, |
||||
|
Illuminate\Bus\BusServiceProvider::class, |
||||
|
Illuminate\Cache\CacheServiceProvider::class, |
||||
|
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, |
||||
|
Illuminate\Cookie\CookieServiceProvider::class, |
||||
|
Illuminate\Database\DatabaseServiceProvider::class, |
||||
|
Illuminate\Encryption\EncryptionServiceProvider::class, |
||||
|
Illuminate\Filesystem\FilesystemServiceProvider::class, |
||||
|
Illuminate\Foundation\Providers\FoundationServiceProvider::class, |
||||
|
Illuminate\Hashing\HashServiceProvider::class, |
||||
|
Illuminate\Mail\MailServiceProvider::class, |
||||
|
Illuminate\Notifications\NotificationServiceProvider::class, |
||||
|
Illuminate\Pagination\PaginationServiceProvider::class, |
||||
|
Illuminate\Pipeline\PipelineServiceProvider::class, |
||||
|
Illuminate\Queue\QueueServiceProvider::class, |
||||
|
Illuminate\Redis\RedisServiceProvider::class, |
||||
|
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, |
||||
|
Illuminate\Session\SessionServiceProvider::class, |
||||
|
Illuminate\Translation\TranslationServiceProvider::class, |
||||
|
Illuminate\Validation\ValidationServiceProvider::class, |
||||
|
Illuminate\View\ViewServiceProvider::class, |
||||
|
|
||||
|
/* |
||||
|
* Package Service Providers... |
||||
|
*/ |
||||
|
|
||||
|
/* |
||||
|
* Application Service Providers... |
||||
|
*/ |
||||
|
App\Providers\AppServiceProvider::class, |
||||
|
App\Providers\AuthServiceProvider::class, |
||||
|
// App\Providers\BroadcastServiceProvider::class,
|
||||
|
App\Providers\EventServiceProvider::class, |
||||
|
App\Providers\RouteServiceProvider::class, |
||||
|
|
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Class Aliases |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This array of class aliases will be registered when this application |
||||
|
| is started. However, feel free to register as many as you wish as |
||||
|
| the aliases are "lazy" loaded so they don't hinder performance. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'aliases' => [ |
||||
|
|
||||
|
'App' => Illuminate\Support\Facades\App::class, |
||||
|
'Arr' => Illuminate\Support\Arr::class, |
||||
|
'Artisan' => Illuminate\Support\Facades\Artisan::class, |
||||
|
'Auth' => Illuminate\Support\Facades\Auth::class, |
||||
|
'Blade' => Illuminate\Support\Facades\Blade::class, |
||||
|
'Broadcast' => Illuminate\Support\Facades\Broadcast::class, |
||||
|
'Bus' => Illuminate\Support\Facades\Bus::class, |
||||
|
'Cache' => Illuminate\Support\Facades\Cache::class, |
||||
|
'Config' => Illuminate\Support\Facades\Config::class, |
||||
|
'Cookie' => Illuminate\Support\Facades\Cookie::class, |
||||
|
'Crypt' => Illuminate\Support\Facades\Crypt::class, |
||||
|
'DB' => Illuminate\Support\Facades\DB::class, |
||||
|
'Eloquent' => Illuminate\Database\Eloquent\Model::class, |
||||
|
'Event' => Illuminate\Support\Facades\Event::class, |
||||
|
'File' => Illuminate\Support\Facades\File::class, |
||||
|
'Gate' => Illuminate\Support\Facades\Gate::class, |
||||
|
'Hash' => Illuminate\Support\Facades\Hash::class, |
||||
|
'Http' => Illuminate\Support\Facades\Http::class, |
||||
|
'Lang' => Illuminate\Support\Facades\Lang::class, |
||||
|
'Log' => Illuminate\Support\Facades\Log::class, |
||||
|
'Mail' => Illuminate\Support\Facades\Mail::class, |
||||
|
'Notification' => Illuminate\Support\Facades\Notification::class, |
||||
|
'Password' => Illuminate\Support\Facades\Password::class, |
||||
|
'Queue' => Illuminate\Support\Facades\Queue::class, |
||||
|
'Redirect' => Illuminate\Support\Facades\Redirect::class, |
||||
|
// 'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
|
'Request' => Illuminate\Support\Facades\Request::class, |
||||
|
'Response' => Illuminate\Support\Facades\Response::class, |
||||
|
'Route' => Illuminate\Support\Facades\Route::class, |
||||
|
'Schema' => Illuminate\Support\Facades\Schema::class, |
||||
|
'Session' => Illuminate\Support\Facades\Session::class, |
||||
|
'Storage' => Illuminate\Support\Facades\Storage::class, |
||||
|
'Str' => Illuminate\Support\Str::class, |
||||
|
'URL' => Illuminate\Support\Facades\URL::class, |
||||
|
'Validator' => Illuminate\Support\Facades\Validator::class, |
||||
|
'View' => Illuminate\Support\Facades\View::class, |
||||
|
|
||||
|
], |
||||
|
|
||||
|
]; |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Cross-Origin Resource Sharing (CORS) Configuration |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure your settings for cross-origin resource sharing |
||||
|
| or "CORS". This determines what cross-origin operations may execute |
||||
|
| in web browsers. You are free to adjust these settings as needed. |
||||
|
| |
||||
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'paths' => ['api/*', 'sanctum/csrf-cookie'], |
||||
|
|
||||
|
'allowed_methods' => ['*'], |
||||
|
|
||||
|
'allowed_origins' => ['*'], |
||||
|
|
||||
|
'allowed_origins_patterns' => [], |
||||
|
|
||||
|
'allowed_headers' => ['*'], |
||||
|
|
||||
|
'exposed_headers' => [], |
||||
|
|
||||
|
'max_age' => 0, |
||||
|
|
||||
|
'supports_credentials' => false, |
||||
|
|
||||
|
]; |
@ -0,0 +1,72 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Default Filesystem Disk |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may specify the default filesystem disk that should be used |
||||
|
| by the framework. The "local" disk, as well as a variety of cloud |
||||
|
| based disks are available to your application. Just store away! |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'default' => env('FILESYSTEM_DRIVER', 'local'), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Filesystem Disks |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure as many filesystem "disks" as you wish, and you |
||||
|
| may even configure multiple disks of the same driver. Defaults have |
||||
|
| been setup for each driver as an example of the required options. |
||||
|
| |
||||
|
| Supported Drivers: "local", "ftp", "sftp", "s3" |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'disks' => [ |
||||
|
|
||||
|
'local' => [ |
||||
|
'driver' => 'local', |
||||
|
'root' => storage_path('app'), |
||||
|
], |
||||
|
|
||||
|
'public' => [ |
||||
|
'driver' => 'local', |
||||
|
'root' => storage_path('app/public'), |
||||
|
'url' => env('APP_URL').'/storage', |
||||
|
'visibility' => 'public', |
||||
|
], |
||||
|
|
||||
|
's3' => [ |
||||
|
'driver' => 's3', |
||||
|
'key' => env('AWS_ACCESS_KEY_ID'), |
||||
|
'secret' => env('AWS_SECRET_ACCESS_KEY'), |
||||
|
'region' => env('AWS_DEFAULT_REGION'), |
||||
|
'bucket' => env('AWS_BUCKET'), |
||||
|
'url' => env('AWS_URL'), |
||||
|
'endpoint' => env('AWS_ENDPOINT'), |
||||
|
], |
||||
|
|
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Symbolic Links |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure the symbolic links that will be created when the |
||||
|
| `storage:link` Artisan command is executed. The array keys should be |
||||
|
| the locations of the links and the values should be their targets. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'links' => [ |
||||
|
public_path('storage') => storage_path('app/public'), |
||||
|
], |
||||
|
|
||||
|
]; |
@ -0,0 +1,104 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Monolog\Handler\NullHandler; |
||||
|
use Monolog\Handler\StreamHandler; |
||||
|
use Monolog\Handler\SyslogUdpHandler; |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Default Log Channel |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This option defines the default log channel that gets used when writing |
||||
|
| messages to the logs. The name specified in this option should match |
||||
|
| one of the channels defined in the "channels" configuration array. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'default' => env('LOG_CHANNEL', 'stack'), |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Log Channels |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure the log channels for your application. Out of |
||||
|
| the box, Laravel uses the Monolog PHP logging library. This gives |
||||
|
| you a variety of powerful log handlers / formatters to utilize. |
||||
|
| |
||||
|
| Available Drivers: "single", "daily", "slack", "syslog", |
||||
|
| "errorlog", "monolog", |
||||
|
| "custom", "stack" |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'channels' => [ |
||||
|
'stack' => [ |
||||
|
'driver' => 'stack', |
||||
|
'channels' => ['single'], |
||||
|
'ignore_exceptions' => false, |
||||
|
], |
||||
|
|
||||
|
'single' => [ |
||||
|
'driver' => 'single', |
||||
|
'path' => storage_path('logs/laravel.log'), |
||||
|
'level' => env('LOG_LEVEL', 'debug'), |
||||
|
], |
||||
|
|
||||
|
'daily' => [ |
||||
|
'driver' => 'daily', |
||||
|
'path' => storage_path('logs/laravel.log'), |
||||
|
'level' => env('LOG_LEVEL', 'debug'), |
||||
|
'days' => 14, |
||||
|
], |
||||
|
|
||||
|
'slack' => [ |
||||
|
'driver' => 'slack', |
||||
|
'url' => env('LOG_SLACK_WEBHOOK_URL'), |
||||
|
'username' => 'Laravel Log', |
||||
|
'emoji' => ':boom:', |
||||
|
'level' => env('LOG_LEVEL', 'critical'), |
||||
|
], |
||||
|
|
||||
|
'papertrail' => [ |
||||
|
'driver' => 'monolog', |
||||
|
'level' => env('LOG_LEVEL', 'debug'), |
||||
|
'handler' => SyslogUdpHandler::class, |
||||
|
'handler_with' => [ |
||||
|
'host' => env('PAPERTRAIL_URL'), |
||||
|
'port' => env('PAPERTRAIL_PORT'), |
||||
|
], |
||||
|
], |
||||
|
|
||||
|
'stderr' => [ |
||||
|
'driver' => 'monolog', |
||||
|
'handler' => StreamHandler::class, |
||||
|
'formatter' => env('LOG_STDERR_FORMATTER'), |
||||
|
'with' => [ |
||||
|
'stream' => 'php://stderr', |
||||
|
], |
||||
|
], |
||||
|
|
||||
|
'syslog' => [ |
||||
|
'driver' => 'syslog', |
||||
|
'level' => env('LOG_LEVEL', 'debug'), |
||||
|
], |
||||
|
|
||||
|
'errorlog' => [ |
||||
|
'driver' => 'errorlog', |
||||
|
'level' => env('LOG_LEVEL', 'debug'), |
||||
|
], |
||||
|
|
||||
|
'null' => [ |
||||
|
'driver' => 'monolog', |
||||
|
'handler' => NullHandler::class, |
||||
|
], |
||||
|
|
||||
|
'emergency' => [ |
||||
|
'path' => storage_path('logs/laravel.log'), |
||||
|
], |
||||
|
], |
||||
|
|
||||
|
]; |
@ -0,0 +1,33 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Third Party Services |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| This file is for storing the credentials for third party services such |
||||
|
| as Mailgun, Postmark, AWS and more. This file provides the de facto |
||||
|
| location for this type of information, allowing packages to have |
||||
|
| a conventional file to locate the various service credentials. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'mailgun' => [ |
||||
|
'domain' => env('MAILGUN_DOMAIN'), |
||||
|
'secret' => env('MAILGUN_SECRET'), |
||||
|
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), |
||||
|
], |
||||
|
|
||||
|
'postmark' => [ |
||||
|
'token' => env('POSTMARK_TOKEN'), |
||||
|
], |
||||
|
|
||||
|
'ses' => [ |
||||
|
'key' => env('AWS_ACCESS_KEY_ID'), |
||||
|
'secret' => env('AWS_SECRET_ACCESS_KEY'), |
||||
|
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), |
||||
|
], |
||||
|
|
||||
|
]; |
@ -0,0 +1,73 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use PhpAmqpLib\Message\AMQPMessage; |
||||
|
|
||||
|
return [ |
||||
|
/* Default connection */ |
||||
|
'default' => env('AMQP_CONNECTION', 'rabbitmq'), |
||||
|
|
||||
|
/*Available connections*/ |
||||
|
'connections' => [ |
||||
|
|
||||
|
'rabbitmq' => [ |
||||
|
'connection' => [ |
||||
|
'host' => env('AMQP_HOST', 'base-rabbitmq'), |
||||
|
'port' => env('AMQP_PORT', 5672), |
||||
|
'username' => env('AMQP_USERNAME', 'root'), |
||||
|
'password' => env('AMQP_PASSWORD', 'root'), |
||||
|
'vhost' => env('AMQP_VHOST', '/'), |
||||
|
'connect_options' => [], |
||||
|
'ssl_options' => [], |
||||
|
'ssl_protocol' => env('AMQP_SSL_PROTOCOL', 'ssl'), |
||||
|
], |
||||
|
|
||||
|
'channel_id' => null, |
||||
|
|
||||
|
'message' => [ |
||||
|
'content_type' => 'text/plain', |
||||
|
'delivery_mode' => env('AMQP_MESSAGE_DELIVERY_MODE', AMQPMessage::DELIVERY_MODE_PERSISTENT), |
||||
|
'content_encoding' => 'UTF-8', |
||||
|
], |
||||
|
|
||||
|
'exchange' => [ |
||||
|
'name' => env('AMQP_EXCHANGE_NAME', 'activity_exchange'), |
||||
|
'declare' => env('AMQP_EXCHANGE_DECLARE', false), |
||||
|
'type' => env('AMQP_EXCHANGE_TYPE', 'headers'), |
||||
|
'passive' => env('AMQP_EXCHANGE_PASSIVE', false), |
||||
|
'durable' => env('AMQP_EXCHANGE_DURABLE', true), |
||||
|
'auto_delete' => env('AMQP_EXCHANGE_AUTO_DEL', false), |
||||
|
'internal' => env('AMQP_EXCHANGE_INTERNAL', false), |
||||
|
'nowait' => env('AMQP_EXCHANGE_NOWAIT', false), |
||||
|
'properties' => [], |
||||
|
], |
||||
|
|
||||
|
'queue' => [ |
||||
|
'declare' => env('AMQP_QUEUE_DECLARE', false), |
||||
|
'passive' => env('AMQP_QUEUE_PASSIVE', false), |
||||
|
'durable' => env('AMQP_QUEUE_DURABLE', true), |
||||
|
'exclusive' => env('AMQP_QUEUE_EXCLUSIVE', false), |
||||
|
'auto_delete' => env('AMQP_QUEUE_AUTO_DEL', false), |
||||
|
'nowait' => env('AMQP_QUEUE_NOWAIT', false), |
||||
|
'd_properties' => [], // queue_declare properties/arguments
|
||||
|
'b_properties' => [], // queue_bind properties/arguments
|
||||
|
], |
||||
|
|
||||
|
'consumer' => [ |
||||
|
'tag' => env('AMQP_CONSUMER_TAG', ''), |
||||
|
'no_local' => env('AMQP_CONSUMER_NO_LOCAL', false), |
||||
|
'no_ack' => env('AMQP_CONSUMER_NO_ACK', false), |
||||
|
'exclusive' => env('AMQP_CONSUMER_EXCLUSIVE', false), |
||||
|
'nowait' => env('AMQP_CONSUMER_NOWAIT', false), |
||||
|
'ticket' => null, |
||||
|
'properties' => [], |
||||
|
], |
||||
|
|
||||
|
'qos' => [ |
||||
|
'enabled' => env('AMQP_QOS_ENABLED', false), |
||||
|
'qos_prefetch_size' => env('AMQP_QOS_PREF_SIZE', 0), |
||||
|
'qos_prefetch_count' => env('AMQP_QOS_PREF_COUNT', 1), |
||||
|
'qos_a_global' => env('AMQP_QOS_GLOBAL', false), |
||||
|
], |
||||
|
], |
||||
|
], |
||||
|
]; |
@ -1,232 +1,19 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
return [ |
return [ |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Name |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This value is the name of your application. This value is used when the |
|
||||
| framework needs to place the application's name in a notification or |
|
||||
| any other location as required by the application or its packages. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'name' => env('APP_NAME', 'Laravel'), |
'name' => env('APP_NAME', 'Laravel'), |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Environment |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This value determines the "environment" your application is currently |
|
||||
| running in. This may determine how you prefer to configure various |
|
||||
| services the application utilizes. Set this in your ".env" file. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'env' => env('APP_ENV', 'production'), |
'env' => env('APP_ENV', 'production'), |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Debug Mode |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| When your application is in debug mode, detailed error messages with |
|
||||
| stack traces will be shown on every error that occurs within your |
|
||||
| application. If disabled, a simple generic error page is shown. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'debug' => (bool) env('APP_DEBUG', false), |
'debug' => (bool) env('APP_DEBUG', false), |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application URL |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This URL is used by the console to properly generate URLs when using |
|
||||
| the Artisan command line tool. You should set this to the root of |
|
||||
| your application so that it is used when running Artisan tasks. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'url' => env('APP_URL', 'http://localhost'), |
|
||||
|
|
||||
'asset_url' => env('ASSET_URL', null), |
|
||||
|
'timezone' => 'Asia/Tehran', |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Timezone |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| Here you may specify the default timezone for your application, which |
|
||||
| will be used by the PHP date and date-time functions. We have gone |
|
||||
| ahead and set this to a sensible default for you out of the box. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'timezone' => 'UTC', |
|
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Locale Configuration |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| The application locale determines the default locale that will be used |
|
||||
| by the translation service provider. You are free to set this value |
|
||||
| to any of the locales which will be supported by the application. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'locale' => 'en', |
|
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Application Fallback Locale |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| The fallback locale determines the locale to use when the current one |
|
||||
| is not available. You may change the value to correspond to any of |
|
||||
| the language folders that are provided through your application. |
|
||||
| |
|
||||
*/ |
|
||||
|
'locale' => 'fa', |
||||
|
|
||||
'fallback_locale' => 'en', |
'fallback_locale' => 'en', |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Faker Locale |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This locale will be used by the Faker PHP library when generating fake |
|
||||
| data for your database seeds. For example, this will be used to get |
|
||||
| localized telephone numbers, street address information and more. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'faker_locale' => 'en_US', |
|
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Encryption Key |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This key is used by the Illuminate encrypter service and should be set |
|
||||
| to a random, 32 character string, otherwise these encrypted strings |
|
||||
| will not be safe. Please do this before deploying an application! |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'key' => env('APP_KEY'), |
|
||||
|
|
||||
'cipher' => 'AES-256-CBC', |
|
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Autoloaded Service Providers |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| The service providers listed here will be automatically loaded on the |
|
||||
| request to your application. Feel free to add your own services to |
|
||||
| this array to grant expanded functionality to your applications. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'providers' => [ |
|
||||
|
|
||||
/* |
|
||||
* Laravel Framework Service Providers... |
|
||||
*/ |
|
||||
Illuminate\Auth\AuthServiceProvider::class, |
|
||||
Illuminate\Broadcasting\BroadcastServiceProvider::class, |
|
||||
Illuminate\Bus\BusServiceProvider::class, |
|
||||
Illuminate\Cache\CacheServiceProvider::class, |
|
||||
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, |
|
||||
Illuminate\Cookie\CookieServiceProvider::class, |
|
||||
Illuminate\Database\DatabaseServiceProvider::class, |
|
||||
Illuminate\Encryption\EncryptionServiceProvider::class, |
|
||||
Illuminate\Filesystem\FilesystemServiceProvider::class, |
|
||||
Illuminate\Foundation\Providers\FoundationServiceProvider::class, |
|
||||
Illuminate\Hashing\HashServiceProvider::class, |
|
||||
Illuminate\Mail\MailServiceProvider::class, |
|
||||
Illuminate\Notifications\NotificationServiceProvider::class, |
|
||||
Illuminate\Pagination\PaginationServiceProvider::class, |
|
||||
Illuminate\Pipeline\PipelineServiceProvider::class, |
|
||||
Illuminate\Queue\QueueServiceProvider::class, |
|
||||
Illuminate\Redis\RedisServiceProvider::class, |
|
||||
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, |
|
||||
Illuminate\Session\SessionServiceProvider::class, |
|
||||
Illuminate\Translation\TranslationServiceProvider::class, |
|
||||
Illuminate\Validation\ValidationServiceProvider::class, |
|
||||
Illuminate\View\ViewServiceProvider::class, |
|
||||
|
|
||||
/* |
|
||||
* Package Service Providers... |
|
||||
*/ |
|
||||
|
|
||||
/* |
|
||||
* Application Service Providers... |
|
||||
*/ |
|
||||
App\Providers\AppServiceProvider::class, |
|
||||
App\Providers\AuthServiceProvider::class, |
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
|
||||
App\Providers\EventServiceProvider::class, |
|
||||
App\Providers\RouteServiceProvider::class, |
|
||||
|
|
||||
], |
|
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Class Aliases |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This array of class aliases will be registered when this application |
|
||||
| is started. However, feel free to register as many as you wish as |
|
||||
| the aliases are "lazy" loaded so they don't hinder performance. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'aliases' => [ |
|
||||
|
|
||||
'App' => Illuminate\Support\Facades\App::class, |
|
||||
'Arr' => Illuminate\Support\Arr::class, |
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class, |
|
||||
'Auth' => Illuminate\Support\Facades\Auth::class, |
|
||||
'Blade' => Illuminate\Support\Facades\Blade::class, |
|
||||
'Broadcast' => Illuminate\Support\Facades\Broadcast::class, |
|
||||
'Bus' => Illuminate\Support\Facades\Bus::class, |
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class, |
|
||||
'Config' => Illuminate\Support\Facades\Config::class, |
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class, |
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class, |
|
||||
'DB' => Illuminate\Support\Facades\DB::class, |
|
||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class, |
|
||||
'Event' => Illuminate\Support\Facades\Event::class, |
|
||||
'File' => Illuminate\Support\Facades\File::class, |
|
||||
'Gate' => Illuminate\Support\Facades\Gate::class, |
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class, |
|
||||
'Http' => Illuminate\Support\Facades\Http::class, |
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class, |
|
||||
'Log' => Illuminate\Support\Facades\Log::class, |
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class, |
|
||||
'Notification' => Illuminate\Support\Facades\Notification::class, |
|
||||
'Password' => Illuminate\Support\Facades\Password::class, |
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class, |
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class, |
|
||||
// 'Redis' => Illuminate\Support\Facades\Redis::class,
|
|
||||
'Request' => Illuminate\Support\Facades\Request::class, |
|
||||
'Response' => Illuminate\Support\Facades\Response::class, |
|
||||
'Route' => Illuminate\Support\Facades\Route::class, |
|
||||
'Schema' => Illuminate\Support\Facades\Schema::class, |
|
||||
'Session' => Illuminate\Support\Facades\Session::class, |
|
||||
'Storage' => Illuminate\Support\Facades\Storage::class, |
|
||||
'Str' => Illuminate\Support\Str::class, |
|
||||
'URL' => Illuminate\Support\Facades\URL::class, |
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class, |
|
||||
'View' => Illuminate\Support\Facades\View::class, |
|
||||
|
|
||||
], |
|
||||
|
'faker_locale' => 'fa_IR', |
||||
|
|
||||
|
'cache_ttl' => 60 |
||||
]; |
]; |
@ -1,34 +1,59 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
return [ |
return [ |
||||
|
|
||||
/* |
/* |
||||
|-------------------------------------------------------------------------- |
|-------------------------------------------------------------------------- |
||||
| Cross-Origin Resource Sharing (CORS) Configuration |
|
||||
|
| Laravel CORS Options |
||||
|-------------------------------------------------------------------------- |
|-------------------------------------------------------------------------- |
||||
| |
| |
||||
| Here you may configure your settings for cross-origin resource sharing |
|
||||
| or "CORS". This determines what cross-origin operations may execute |
|
||||
| in web browsers. You are free to adjust these settings as needed. |
|
||||
|
| The allowed_methods and allowed_headers options are case-insensitive. |
||||
|
| |
||||
|
| You don't need to provide both allowed_origins and allowed_origins_patterns. |
||||
|
| If one of the strings passed matches, it is considered a valid origin. |
||||
| |
| |
||||
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS |
|
||||
|
| If ['*'] is provided to allowed_methods, allowed_origins or allowed_headers |
||||
|
| all methods / origins / headers are allowed. |
||||
| |
| |
||||
*/ |
*/ |
||||
|
|
||||
'paths' => ['api/*', 'sanctum/csrf-cookie'], |
|
||||
|
/* |
||||
|
* You can enable CORS for 1 or multiple paths. |
||||
|
* Example: ['api/*'] |
||||
|
*/ |
||||
|
'paths' => ['/*'], |
||||
|
|
||||
|
/* |
||||
|
* Matches the request method. `['*']` allows all methods. |
||||
|
*/ |
||||
'allowed_methods' => ['*'], |
'allowed_methods' => ['*'], |
||||
|
|
||||
|
/* |
||||
|
* Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com` |
||||
|
*/ |
||||
'allowed_origins' => ['*'], |
'allowed_origins' => ['*'], |
||||
|
|
||||
'allowed_origins_patterns' => [], |
|
||||
|
/* |
||||
|
* Patterns that can be used with `preg_match` to match the origin. |
||||
|
*/ |
||||
|
'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'], |
||||
|
|
||||
|
/* |
||||
|
* Sets the Access-Control-Allow-Headers response header. `['*']` allows all headers. |
||||
|
*/ |
||||
'allowed_headers' => ['*'], |
'allowed_headers' => ['*'], |
||||
|
|
||||
|
/* |
||||
|
* Sets the Access-Control-Expose-Headers response header with these headers. |
||||
|
*/ |
||||
'exposed_headers' => [], |
'exposed_headers' => [], |
||||
|
|
||||
|
/* |
||||
|
* Sets the Access-Control-Max-Age response header when > 0. |
||||
|
*/ |
||||
'max_age' => 0, |
'max_age' => 0, |
||||
|
|
||||
|
/* |
||||
|
* Sets the Access-Control-Allow-Credentials header. |
||||
|
*/ |
||||
'supports_credentials' => false, |
'supports_credentials' => false, |
||||
|
|
||||
]; |
]; |
@ -0,0 +1,172 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Torann\GeoIP\Services\IPApi; |
||||
|
use Torann\GeoIP\Services\IPData; |
||||
|
use Torann\GeoIP\Services\IPFinder; |
||||
|
use Torann\GeoIP\Services\IPGeoLocation; |
||||
|
use Torann\GeoIP\Services\MaxMindDatabase; |
||||
|
use Torann\GeoIP\Services\MaxMindWebService; |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Logging Configuration |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure the log settings for when a location is not found |
||||
|
| for the IP provided. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'log_failures' => false, |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Include Currency in Results |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| When enabled the system will do it's best in deciding the user's currency |
||||
|
| by matching their ISO code to a preset list of currencies. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'include_currency' => false, |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Default Service |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may specify the default storage driver that should be used |
||||
|
| by the framework. |
||||
|
| |
||||
|
| Supported: "maxmind_database", "maxmind_api", "ipapi" |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'service' => 'ipapi', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Storage Specific Configuration |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may configure as many storage drivers as you wish. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'services' => [ |
||||
|
|
||||
|
'maxmind_database' => [ |
||||
|
'class' => MaxMindDatabase::class, |
||||
|
'database_path' => storage_path('app/geoip.mmdb'), |
||||
|
'update_url' => sprintf('https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=%s&suffix=tar.gz', env('MAXMIND_LICENSE_KEY')), |
||||
|
'locales' => ['en'], |
||||
|
], |
||||
|
|
||||
|
'maxmind_api' => [ |
||||
|
'class' => MaxMindWebService::class, |
||||
|
'user_id' => env('MAXMIND_USER_ID'), |
||||
|
'license_key' => env('MAXMIND_LICENSE_KEY'), |
||||
|
'locales' => ['en'], |
||||
|
], |
||||
|
|
||||
|
'ipapi' => [ |
||||
|
'class' => IPApi::class, |
||||
|
'secure' => true, |
||||
|
'key' => env('IPAPI_KEY'), |
||||
|
'continent_path' => storage_path('app/continents.json'), |
||||
|
'lang' => 'en', |
||||
|
], |
||||
|
|
||||
|
'ipgeolocation' => [ |
||||
|
'class' => IPGeoLocation::class, |
||||
|
'secure' => true, |
||||
|
'key' => env('IPGEOLOCATION_KEY'), |
||||
|
'continent_path' => storage_path('app/continents.json'), |
||||
|
'lang' => 'en', |
||||
|
], |
||||
|
|
||||
|
'ipdata' => [ |
||||
|
'class' => IPData::class, |
||||
|
'key' => env('IPDATA_API_KEY'), |
||||
|
'secure' => true, |
||||
|
], |
||||
|
|
||||
|
'ipfinder' => [ |
||||
|
'class' => IPFinder::class, |
||||
|
'key' => env('IPFINDER_API_KEY'), |
||||
|
'secure' => true, |
||||
|
'locales' => ['en'], |
||||
|
], |
||||
|
|
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Default Cache Driver |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Here you may specify the type of caching that should be used |
||||
|
| by the package. |
||||
|
| |
||||
|
| Options: |
||||
|
| |
||||
|
| all - All location are cached |
||||
|
| some - Cache only the requesting user |
||||
|
| none - Disable cached |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'cache' => 'none', |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Cache Tags |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Cache tags are not supported when using the file or database cache |
||||
|
| drivers in Laravel. This is done so that only locations can be cleared. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
// 'cache_tags' => [''],
|
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Cache Expiration |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Define how long cached location are valid. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'cache_expires' => 30, |
||||
|
|
||||
|
/* |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| Default Location |
||||
|
|-------------------------------------------------------------------------- |
||||
|
| |
||||
|
| Return when a location is not found. |
||||
|
| |
||||
|
*/ |
||||
|
|
||||
|
'default_location' => [ |
||||
|
'ip' => '127.0.0.0', |
||||
|
'iso_code' => 'IRN', |
||||
|
'country' => 'Islamic Republic of Iran', |
||||
|
'city' => 'Tehran', |
||||
|
'state' => 'teh', |
||||
|
'state_name' => 'Connecticut', |
||||
|
'postal_code' => '513', |
||||
|
'lat' => 35.6892, |
||||
|
'lon' => 51.3890, |
||||
|
'timezone' => 'Asia/Tehran', |
||||
|
'continent' => 'Asia', |
||||
|
'default' => true, |
||||
|
'currency' => 'IRR', |
||||
|
], |
||||
|
|
||||
|
]; |
@ -1,104 +1,44 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
use Monolog\Handler\NullHandler; |
|
||||
use Monolog\Handler\StreamHandler; |
|
||||
use Monolog\Handler\SyslogUdpHandler; |
|
||||
|
use Monolog\Handler\AmqpHandler; |
||||
|
use PhpAmqpLib\Channel\AMQPChannel; |
||||
|
use App\HiLib\Logger\CreateCustomLogger; |
||||
|
use PhpAmqpLib\Connection\AMQPStreamConnection; |
||||
|
|
||||
return [ |
return [ |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Default Log Channel |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This option defines the default log channel that gets used when writing |
|
||||
| messages to the logs. The name specified in this option should match |
|
||||
| one of the channels defined in the "channels" configuration array. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'default' => env('LOG_CHANNEL', 'stack'), |
'default' => env('LOG_CHANNEL', 'stack'), |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Log Channels |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| Here you may configure the log channels for your application. Out of |
|
||||
| the box, Laravel uses the Monolog PHP logging library. This gives |
|
||||
| you a variety of powerful log handlers / formatters to utilize. |
|
||||
| |
|
||||
| Available Drivers: "single", "daily", "slack", "syslog", |
|
||||
| "errorlog", "monolog", |
|
||||
| "custom", "stack" |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'channels' => [ |
'channels' => [ |
||||
|
'hi' => [ |
||||
|
'driver' => 'custom', |
||||
|
'via' => CreateCustomLogger::class, |
||||
|
'handler' => AmqpHandler::class, |
||||
|
'with' => [ |
||||
|
'exchange' => new AMQPChannel( |
||||
|
new AMQPStreamConnection("base-rabbitmq", 5672, 'root', 'root') |
||||
|
), |
||||
|
'exchangeName' => 'log_exchange' |
||||
|
], |
||||
|
], |
||||
|
|
||||
'stack' => [ |
'stack' => [ |
||||
'driver' => 'stack', |
'driver' => 'stack', |
||||
'channels' => ['single'], |
|
||||
|
'channels' => ['hi', 'daily',], |
||||
'ignore_exceptions' => false, |
'ignore_exceptions' => false, |
||||
], |
], |
||||
|
|
||||
'single' => [ |
'single' => [ |
||||
'driver' => 'single', |
'driver' => 'single', |
||||
'path' => storage_path('logs/laravel.log'), |
'path' => storage_path('logs/laravel.log'), |
||||
'level' => env('LOG_LEVEL', 'debug'), |
|
||||
|
'level' => 'debug', |
||||
], |
], |
||||
|
|
||||
'daily' => [ |
'daily' => [ |
||||
'driver' => 'daily', |
'driver' => 'daily', |
||||
'path' => storage_path('logs/laravel.log'), |
'path' => storage_path('logs/laravel.log'), |
||||
'level' => env('LOG_LEVEL', 'debug'), |
|
||||
|
'level' => 'debug', |
||||
'days' => 14, |
'days' => 14, |
||||
], |
], |
||||
|
|
||||
'slack' => [ |
|
||||
'driver' => 'slack', |
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'), |
|
||||
'username' => 'Laravel Log', |
|
||||
'emoji' => ':boom:', |
|
||||
'level' => env('LOG_LEVEL', 'critical'), |
|
||||
], |
|
||||
|
|
||||
'papertrail' => [ |
|
||||
'driver' => 'monolog', |
|
||||
'level' => env('LOG_LEVEL', 'debug'), |
|
||||
'handler' => SyslogUdpHandler::class, |
|
||||
'handler_with' => [ |
|
||||
'host' => env('PAPERTRAIL_URL'), |
|
||||
'port' => env('PAPERTRAIL_PORT'), |
|
||||
], |
|
||||
], |
|
||||
|
|
||||
'stderr' => [ |
|
||||
'driver' => 'monolog', |
|
||||
'handler' => StreamHandler::class, |
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'), |
|
||||
'with' => [ |
|
||||
'stream' => 'php://stderr', |
|
||||
], |
|
||||
], |
|
||||
|
|
||||
'syslog' => [ |
|
||||
'driver' => 'syslog', |
|
||||
'level' => env('LOG_LEVEL', 'debug'), |
|
||||
], |
|
||||
|
|
||||
'errorlog' => [ |
|
||||
'driver' => 'errorlog', |
|
||||
'level' => env('LOG_LEVEL', 'debug'), |
|
||||
], |
|
||||
|
|
||||
'null' => [ |
|
||||
'driver' => 'monolog', |
|
||||
'handler' => NullHandler::class, |
|
||||
], |
|
||||
|
|
||||
'emergency' => [ |
|
||||
'path' => storage_path('logs/laravel.log'), |
|
||||
], |
|
||||
], |
], |
||||
|
|
||||
]; |
]; |
@ -0,0 +1,179 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
* The disk on which to store added files and derived images by default. Choose |
||||
|
* one or more of the disks you've configured in config/filesystems.php. |
||||
|
*/ |
||||
|
'disk_name' => env('MEDIA_DISK', 'public'), |
||||
|
|
||||
|
/* |
||||
|
* The maximum file size of an item in bytes. |
||||
|
* Adding a larger file will result in an exception. |
||||
|
*/ |
||||
|
'max_file_size' => 1024 * 1024 * 10, |
||||
|
|
||||
|
/* |
||||
|
* This queue will be used to generate derived and responsive images. |
||||
|
* Leave empty to use the default queue. |
||||
|
*/ |
||||
|
'queue_name' => '', |
||||
|
|
||||
|
/* |
||||
|
* By default all conversions will be performed on a queue. |
||||
|
*/ |
||||
|
'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true), |
||||
|
|
||||
|
/* |
||||
|
* The fully qualified class name of the media model. |
||||
|
*/ |
||||
|
'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class, |
||||
|
|
||||
|
'remote' => [ |
||||
|
/* |
||||
|
* Any extra headers that should be included when uploading media to |
||||
|
* a remote disk. Even though supported headers may vary between |
||||
|
* different drivers, a sensible default has been provided. |
||||
|
* |
||||
|
* Supported by S3: CacheControl, Expires, StorageClass, |
||||
|
* ServerSideEncryption, Metadata, ACL, ContentEncoding |
||||
|
*/ |
||||
|
'extra_headers' => [ |
||||
|
'CacheControl' => 'max-age=604800', |
||||
|
], |
||||
|
], |
||||
|
|
||||
|
'responsive_images' => [ |
||||
|
|
||||
|
/* |
||||
|
* This class is responsible for calculating the target widths of the responsive |
||||
|
* images. By default we optimize for filesize and create variations that each are 20% |
||||
|
* smaller than the previous one. More info in the documentation. |
||||
|
* |
||||
|
* https://docs.spatie.be/laravel-medialibrary/v8/advanced-usage/generating-responsive-images |
||||
|
*/ |
||||
|
'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class, |
||||
|
|
||||
|
/* |
||||
|
* By default rendering media to a responsive image will add some javascript and a tiny placeholder. |
||||
|
* This ensures that the browser can already determine the correct layout. |
||||
|
*/ |
||||
|
'use_tiny_placeholders' => true, |
||||
|
|
||||
|
/* |
||||
|
* This class will generate the tiny placeholder used for progressive image loading. By default |
||||
|
* the media library will use a tiny blurred jpg image. |
||||
|
*/ |
||||
|
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class, |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* When converting Media instances to response the media library will add |
||||
|
* a `loading` attribute to the `img` tag. Here you can set the default |
||||
|
* value of that attribute. |
||||
|
* |
||||
|
* Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction. |
||||
|
* |
||||
|
* More info: https://css-tricks.com/native-lazy-loading/ |
||||
|
*/ |
||||
|
'default_loading_attribute_value' => null, |
||||
|
|
||||
|
/* |
||||
|
* This is the class that is responsible for naming conversion files. By default, |
||||
|
* it will use the filename of the original and concatenate the conversion name to it. |
||||
|
*/ |
||||
|
'conversion_file_namer' => App\Utilities\Avatar\DefaultConversionFileNamer::class, |
||||
|
|
||||
|
/* |
||||
|
* The class that contains the strategy for determining a media file's path. |
||||
|
*/ |
||||
|
'path_generator' => App\Utilities\Avatar\DefaultPathGenerator::class, |
||||
|
|
||||
|
/* |
||||
|
* When urls to files get generated, this class will be called. Use the default |
||||
|
* if your files are stored locally above the site root or on s3. |
||||
|
*/ |
||||
|
'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class, |
||||
|
|
||||
|
/* |
||||
|
* Whether to activate versioning when urls to files get generated. |
||||
|
* When activated, this attaches a ?v=xx query string to the URL. |
||||
|
*/ |
||||
|
'version_urls' => false, |
||||
|
|
||||
|
/* |
||||
|
* The media library will try to optimize all converted images by removing |
||||
|
* metadata and applying a little bit of compression. These are |
||||
|
* the optimizers that will be used by default. |
||||
|
*/ |
||||
|
'image_optimizers' => [ |
||||
|
Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [ |
||||
|
'--strip-all', // this strips out all text information such as comments and EXIF data
|
||||
|
'--all-progressive', // this will make sure the resulting image is a progressive one
|
||||
|
], |
||||
|
Spatie\ImageOptimizer\Optimizers\Pngquant::class => [ |
||||
|
'--force', // required parameter for this package
|
||||
|
], |
||||
|
Spatie\ImageOptimizer\Optimizers\Optipng::class => [ |
||||
|
'-i0', // this will result in a non-interlaced, progressive scanned image
|
||||
|
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
|
||||
|
'-quiet', // required parameter for this package
|
||||
|
], |
||||
|
Spatie\ImageOptimizer\Optimizers\Svgo::class => [ |
||||
|
'--disable=cleanupIDs', // disabling because it is known to cause troubles
|
||||
|
], |
||||
|
Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [ |
||||
|
'-b', // required parameter for this package
|
||||
|
'-O3', // this produces the slowest but best results
|
||||
|
], |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* These generators will be used to create an image of media files. |
||||
|
*/ |
||||
|
'image_generators' => [ |
||||
|
Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class, |
||||
|
Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class, |
||||
|
Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class, |
||||
|
Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class, |
||||
|
Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class, |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* The engine that should perform the image conversions. |
||||
|
* Should be either `gd` or `imagick`. |
||||
|
*/ |
||||
|
'image_driver' => env('IMAGE_DRIVER', 'gd'), |
||||
|
|
||||
|
/* |
||||
|
* FFMPEG & FFProbe binaries paths, only used if you try to generate video |
||||
|
* thumbnails and have installed the php-ffmpeg/php-ffmpeg composer |
||||
|
* dependency. |
||||
|
*/ |
||||
|
'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'), |
||||
|
'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'), |
||||
|
|
||||
|
/* |
||||
|
* The path where to store temporary files while performing image conversions. |
||||
|
* If set to null, storage_path('media-library/temp') will be used. |
||||
|
*/ |
||||
|
'temporary_directory_path' => null, |
||||
|
|
||||
|
/* |
||||
|
* Here you can override the class names of the jobs used by this package. Make sure |
||||
|
* your custom jobs extend the ones provided by the package. |
||||
|
*/ |
||||
|
'jobs' => [ |
||||
|
'perform_conversions' => Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class, |
||||
|
'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class, |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* When using the addMediaFromUrl method you may want to replace the default downloader. |
||||
|
* This is particularly useful when the url of the image is behind a firewall and |
||||
|
* need to add additional flags, possibly using curl. |
||||
|
*/ |
||||
|
'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class, |
||||
|
|
||||
|
]; |
@ -0,0 +1,38 @@ |
|||||
|
<?php |
||||
|
/** |
||||
|
* @see https://github.com/spatie/laravel-query-builder |
||||
|
*/ |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
/* |
||||
|
* By default the package will use the `include`, `filter`, `sort` |
||||
|
* and `fields` query parameters as described in the readme. |
||||
|
* |
||||
|
* You can customize these query string parameters here. |
||||
|
*/ |
||||
|
'parameters' => [ |
||||
|
'include' => 'include', |
||||
|
|
||||
|
'filter' => 'filter', |
||||
|
|
||||
|
'sort' => 'sort', |
||||
|
|
||||
|
'fields' => 'fields', |
||||
|
|
||||
|
'append' => 'append', |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* Related model counts are included using the relationship name suffixed with this string. |
||||
|
* For example: GET /users?include=postsCount |
||||
|
*/ |
||||
|
'count_suffix' => 'Count', |
||||
|
|
||||
|
/* |
||||
|
* By default the package will throw an `InvalidFilterQuery` exception when a filter in the |
||||
|
* URL is not allowed in the `allowedFilters()` method. |
||||
|
*/ |
||||
|
'disable_invalid_filter_query_exception' => false, |
||||
|
|
||||
|
]; |
@ -1,33 +1,21 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
return [ |
return [ |
||||
|
|
||||
/* |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| Third Party Services |
|
||||
|-------------------------------------------------------------------------- |
|
||||
| |
|
||||
| This file is for storing the credentials for third party services such |
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto |
|
||||
| location for this type of information, allowing packages to have |
|
||||
| a conventional file to locate the various service credentials. |
|
||||
| |
|
||||
*/ |
|
||||
|
|
||||
'mailgun' => [ |
|
||||
'domain' => env('MAILGUN_DOMAIN'), |
|
||||
'secret' => env('MAILGUN_SECRET'), |
|
||||
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), |
|
||||
|
'task' => 'hi-task-app', |
||||
|
'zarinpal' => [ |
||||
|
'merchant-id' => '68d337b0-4b77-11ea-8409-000c295eb8fc', |
||||
|
'type' => 'zarin-gate', // Types: [zarin-gate || normal]
|
||||
|
'callback-url' => 'http://127.0.0.1:8000/user/v1/callback', |
||||
|
'server' => 'germany', // Servers: [germany || iran || test]
|
||||
|
'email' => 'admin@base.com', |
||||
|
'mobile' => '09123456789', |
||||
|
'description' => env('APP_NAME', 'APP_NAME'), |
||||
|
'sandbox' => true, |
||||
], |
], |
||||
|
|
||||
'postmark' => [ |
|
||||
'token' => env('POSTMARK_TOKEN'), |
|
||||
|
'google' => [ |
||||
|
'client_id' => '1002439248397-oa6hnh25n6qri3q4kst62gvb1k9ki65l.apps.googleusercontent.com', |
||||
|
'client_secret' => 'tKbiyh5hOjYIcj-W1y3N8X5R', |
||||
|
'redirect' => 'http://localhost:8000/user/v1/auth/google/callback', |
||||
], |
], |
||||
|
|
||||
'ses' => [ |
|
||||
'key' => env('AWS_ACCESS_KEY_ID'), |
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'), |
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), |
|
||||
], |
|
||||
|
|
||||
]; |
]; |
@ -0,0 +1,19 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\Business; |
||||
|
use Illuminate\Support\Str; |
||||
|
use Faker\Generator as Faker; |
||||
|
use Illuminate\Database\Eloquent\Factory; |
||||
|
|
||||
|
$factory->define(Business::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'name' => $name = $faker->unique()->company, |
||||
|
'slug' => Str::slug($name) . $faker->numberBetween(1, 100), |
||||
|
'wallet' => random_int(111111, 999999), |
||||
|
'slug' => Str::slug($name) . $faker->numberBetween(1, 100), |
||||
|
'color' => $faker->colorName, |
||||
|
'calculated_at' => \Carbon\Carbon::now()->subMinutes(random_int(1, 1000)), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,19 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\Cost; |
||||
|
use Carbon\Carbon; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(Cost::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => random_int(1,5000), |
||||
|
'type' => $type = $faker->boolean() ? 'users' : 'files', |
||||
|
'month' => jdate(Carbon::now()->subDays(random_int(0,90)))->format("Y-m-01"), |
||||
|
'amount' => random_int(1,1000), |
||||
|
'fee' => $type === 'users' ? enum("business.fee.user") : enum("business.fee.file"), |
||||
|
'duration' => random_int(1,60), |
||||
|
'created_at' => Carbon::now()->subMinutes(random_int(1, 1000)), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,35 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\File; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(File::class, function (Faker $faker) { |
||||
|
$mimes = ['application/pdf', 'video/mp4', 'image/png', 'image/jpeg', 'audio/x-wav']; |
||||
|
$extensions = ['pdf', 'mp4', 'png', 'jpg', 'wav']; |
||||
|
$groups = [ |
||||
|
'pdf' => 'pdf', |
||||
|
'mp4' => 'video', |
||||
|
'png' => 'image', |
||||
|
'jpg' => 'image', |
||||
|
'wav' => 'audio', |
||||
|
]; |
||||
|
$sizes = [1, 5, 128, 256, 1024, 2048]; |
||||
|
$rand_type = $faker->numberBetween(0, 4); |
||||
|
return [ |
||||
|
'user_id' => $faker->numberBetween(1, 200), |
||||
|
'business_id' => $faker->numberBetween(1, 200), |
||||
|
'project_id' => $faker->numberBetween(1, 200), |
||||
|
'attached_to_id' => $faker->numberBetween(1, 100), |
||||
|
'attached_to_table' => enum('tables.tasks.id'), |
||||
|
'disk' => 's3', |
||||
|
'original_name' => $faker->words(1, true), |
||||
|
'name' => $faker->words(1, true), |
||||
|
'extension' => $extension = $extensions[$rand_type], |
||||
|
'mime' => $mimes[$rand_type], |
||||
|
'group' => $groups[$extension], |
||||
|
'size' => $sizes[$faker->numberBetween(0, 5)], |
||||
|
'description' => $faker->text, |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,38 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\Fingerprint; |
||||
|
use Faker\Factory as Faker; |
||||
|
use Illuminate\Database\Eloquent\Factory; |
||||
|
use Illuminate\Support\Arr; |
||||
|
use Illuminate\Support\Str; |
||||
|
|
||||
|
$faker = Faker::create('fa_IR'); |
||||
|
|
||||
|
$factory->define(Fingerprint::class, function () use ($faker) { |
||||
|
$os = [ |
||||
|
$faker->windowsPlatformToken, |
||||
|
$faker->linuxPlatformToken, |
||||
|
$faker->macPlatformToken |
||||
|
]; |
||||
|
|
||||
|
$browsers = [ |
||||
|
$faker->firefox, |
||||
|
$faker->chrome, |
||||
|
$faker->opera, |
||||
|
$faker->safari, |
||||
|
]; |
||||
|
|
||||
|
$detector = new Jenssegers\Agent\Agent(); |
||||
|
|
||||
|
return [ |
||||
|
'user_id' => $faker->numberBetween(1, 1000), |
||||
|
'agent' => $detector->browser(Arr::random($browsers)), |
||||
|
'ip' => $faker->ipv4, |
||||
|
'os' => $detector->platform(Arr::random($browsers)), |
||||
|
'latitude' => $faker->latitude, |
||||
|
'longitude' => $faker->longitude, |
||||
|
'token' => Str::random(60), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,23 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\Project; |
||||
|
use Faker\Generator as Faker; |
||||
|
use Illuminate\Database\Eloquent\Factory; |
||||
|
use Illuminate\Support\Str; |
||||
|
|
||||
|
$factory->define(Project::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => null, |
||||
|
'name' => $name = $faker->words(3, true), |
||||
|
'slug' => Str::slug($name), |
||||
|
'private' => false, |
||||
|
'budget' => 0, |
||||
|
'start' => null, |
||||
|
'finish' => null, |
||||
|
'color' => $faker->colorName, |
||||
|
'active' => rand(0, 1), |
||||
|
'description' => $faker->paragraph, |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
|
use App\Sprint; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(Sprint::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => null, |
||||
|
'name' => $faker->randomElement(['scrum', 'printing', |
||||
|
'agile', 'develop', 'design', 'writing', 'seo', 'sale']), |
||||
|
'active' => rand(0, 1), |
||||
|
'description' => $faker->paragraph, |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use App\System; |
||||
|
use Faker\Generator as Faker; |
||||
|
use Illuminate\Database\Eloquent\Factory; |
||||
|
use Illuminate\Support\Str; |
||||
|
|
||||
|
$factory->define(System::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => null, |
||||
|
'project_id' => null, |
||||
|
'name' => $name = $faker->words(3, true), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
|
use App\Tag; |
||||
|
use Faker\Factory; |
||||
|
|
||||
|
$faker = Factory::create('fa_IR'); |
||||
|
$factory->define(Tag::class, function () use ($faker) { |
||||
|
return [ |
||||
|
'label' => $faker->colorName, |
||||
|
'color' => $faker->colorName, |
||||
|
'business_id' => null, |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,22 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
|
use App\Task; |
||||
|
use Carbon\Carbon; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(Task::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => null, |
||||
|
'creator_id' => null, |
||||
|
'project_id' => null, |
||||
|
'user_id' => null, |
||||
|
'workflow_id' => null, |
||||
|
'name' => $faker->sentences(3, true), |
||||
|
'time' => null, |
||||
|
'cost' => 0, |
||||
|
'completed' => false, |
||||
|
'due_date' => Carbon::now()->toDateTimeString(), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,21 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var Factory $factory */ |
||||
|
|
||||
|
use Carbon\Carbon; |
||||
|
use App\Transaction; |
||||
|
use Faker\Generator as Faker; |
||||
|
use Illuminate\Support\Facades\DB; |
||||
|
|
||||
|
$factory->define(Transaction::class, function (Faker $faker) { |
||||
|
$business_user = DB::selectOne('select business_id, user_id from business_user where level = 4 order by rand() limit 1'); |
||||
|
|
||||
|
return [ |
||||
|
'user_id' => $business_user->user_id, |
||||
|
'business_id' => $business_user->business_id, |
||||
|
'amount' => random_int(1000, 9999), |
||||
|
'succeeded' => $faker->boolean(), |
||||
|
'options' => json_encode([]), |
||||
|
'created_at' => Carbon::now()->subMinutes(random_int(1, 1000)), |
||||
|
]; |
||||
|
}); |
@ -1,47 +1,16 @@ |
|||||
<?php |
<?php |
||||
|
|
||||
namespace Database\Factories; |
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
use App\Models\User; |
|
||||
use Illuminate\Database\Eloquent\Factories\Factory; |
|
||||
use Illuminate\Support\Str; |
|
||||
|
use App\User; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
class UserFactory extends Factory |
|
||||
{ |
|
||||
/** |
|
||||
* The name of the factory's corresponding model. |
|
||||
* |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $model = User::class; |
|
||||
|
|
||||
/** |
|
||||
* Define the model's default state. |
|
||||
* |
|
||||
* @return array |
|
||||
*/ |
|
||||
public function definition() |
|
||||
{ |
|
||||
return [ |
|
||||
'name' => $this->faker->name, |
|
||||
'email' => $this->faker->unique()->safeEmail, |
|
||||
'email_verified_at' => now(), |
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
|
||||
'remember_token' => Str::random(10), |
|
||||
]; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Indicate that the model's email address should be unverified. |
|
||||
* |
|
||||
* @return \Illuminate\Database\Eloquent\Factories\Factory |
|
||||
*/ |
|
||||
public function unverified() |
|
||||
{ |
|
||||
return $this->state(function (array $attributes) { |
|
||||
return [ |
|
||||
'email_verified_at' => null, |
|
||||
]; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
$factory->define(User::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'name' => $faker->name, |
||||
|
'email' => $faker->unique()->safeEmail, |
||||
|
'mobile' => $faker->unique()->phoneNumber, |
||||
|
'username' => $faker->unique()->userName, |
||||
|
'password' => '$2y$10$l8jgLtb7RyDd7wbvxYPsuu7gjo/bLBkBYQhXnkpdmm.SVF3CT00UW', |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
|
use App\Workflow; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(Workflow::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'business_id' => null, |
||||
|
'name' => $faker->randomElement(['scrum', 'printing', |
||||
|
'agile', 'develop', 'design', 'writing', 'seo', 'sale']), |
||||
|
'desc' => $faker->sentences(1, true), |
||||
|
]; |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
/** @var \Illuminate\Database\Eloquent\Factory $factory */ |
||||
|
|
||||
|
use App\Status; |
||||
|
use Faker\Generator as Faker; |
||||
|
|
||||
|
$factory->define(Status::class, function (Faker $faker) { |
||||
|
return [ |
||||
|
'name' => $faker->randomElement(['idea', 'todo', |
||||
|
'doing', 'done', 'pending', 'hold', 'logestik', 'sew']), |
||||
|
'state' => rand(0, 3), |
||||
|
'order' => rand(0, 10), |
||||
|
]; |
||||
|
}); |
@ -1,36 +0,0 @@ |
|||||
<?php |
|
||||
|
|
||||
use Illuminate\Database\Migrations\Migration; |
|
||||
use Illuminate\Database\Schema\Blueprint; |
|
||||
use Illuminate\Support\Facades\Schema; |
|
||||
|
|
||||
class CreateFailedJobsTable extends Migration |
|
||||
{ |
|
||||
/** |
|
||||
* Run the migrations. |
|
||||
* |
|
||||
* @return void |
|
||||
*/ |
|
||||
public function up() |
|
||||
{ |
|
||||
Schema::create('failed_jobs', function (Blueprint $table) { |
|
||||
$table->id(); |
|
||||
$table->string('uuid')->unique(); |
|
||||
$table->text('connection'); |
|
||||
$table->text('queue'); |
|
||||
$table->longText('payload'); |
|
||||
$table->longText('exception'); |
|
||||
$table->timestamp('failed_at')->useCurrent(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Reverse the migrations. |
|
||||
* |
|
||||
* @return void |
|
||||
*/ |
|
||||
public function down() |
|
||||
{ |
|
||||
Schema::dropIfExists('failed_jobs'); |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,38 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class Fingerprints extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('fingerprints', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('user_id'); |
||||
|
$table->string('agent'); |
||||
|
$table->ipAddress('ip'); |
||||
|
$table->string('os'); |
||||
|
$table->decimal('latitude', 10, 2); |
||||
|
$table->decimal('longitude', 11, 2); |
||||
|
$table->char('token', 60)->unique(); |
||||
|
$table->timestamps(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('fingerprints'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateBusinessesTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('businesses', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->string('name'); |
||||
|
$table->string('slug')->unique(); |
||||
|
$table->integer('wallet')->default(0); |
||||
|
$table->unsignedInteger('files_volume')->default(0); |
||||
|
$table->string('color')->nullable(); |
||||
|
$table->string('description')->nullable(); |
||||
|
$table->json('cache')->nullable(); |
||||
|
$table->boolean('has_avatar')->default(false); |
||||
|
$table->timestamp('calculated_at')->nullable(); |
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->nullable(); |
||||
|
$table->timestamp('deleted_at')->nullable(); |
||||
|
}); |
||||
|
|
||||
|
Schema::create('business_user', function (Blueprint $table) { |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->unsignedBigInteger('user_id'); |
||||
|
$table->tinyInteger('level')->default(0); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('businesses'); |
||||
|
Schema::dropIfExists('businesses_user'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateProjectsTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('projects', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->string('name'); |
||||
|
$table->string('slug'); |
||||
|
$table->boolean('private')->default(false); |
||||
|
$table->unsignedBigInteger('budget')->default(0); |
||||
|
$table->string('color')->nullable(); |
||||
|
$table->boolean('active')->nullable(); |
||||
|
$table->text('description')->nullable(); |
||||
|
$table->boolean('has_avatar')->default(false); |
||||
|
|
||||
|
$table->timestamp('start')->nullable(); |
||||
|
$table->timestamp('finish')->nullable(); |
||||
|
|
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->useCurrent(); |
||||
|
$table->timestamp('deleted_at')->nullable(); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
// Only those users that added directly to the project are stored here.
|
||||
|
// Users who are members of the business have access to the projects of that business
|
||||
|
// without being stored here.
|
||||
|
Schema::create('project_user', function (Blueprint $table) { |
||||
|
$table->unsignedBigInteger('project_id'); |
||||
|
$table->unsignedBigInteger('user_id'); |
||||
|
$table->tinyInteger('level')->default(0); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('projects'); |
||||
|
Schema::dropIfExists('project_user'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateStatusesTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('statuses', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->unsignedBigInteger('workflow_id'); |
||||
|
$table->string('name'); |
||||
|
$table->string('state')->default(enum('status.states.inactive.id')); |
||||
|
$table->unsignedSmallInteger('order')->default(0); |
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->useCurrent(); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('statuses'); |
||||
|
// Schema::dropIfExists('status_workflow');
|
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateTagsTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('tags', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->string('label'); |
||||
|
$table->string('color')->nullable(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->useCurrent(); |
||||
|
}); |
||||
|
|
||||
|
Schema::create('tag_task', function (Blueprint $table) { |
||||
|
$table->unsignedBigInteger('tag_id'); |
||||
|
$table->unsignedBigInteger('task_id'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('tags'); |
||||
|
Schema::dropIfExists('tag_task'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateSystemsTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('systems', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->unsignedBigInteger('project_id'); |
||||
|
$table->string('name'); |
||||
|
$table->timestamps(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('systems'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateSprintTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('sprints', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->unsignedBigInteger('project_id'); |
||||
|
$table->string('name'); |
||||
|
$table->text('description')->nullable(); |
||||
|
$table->date('started_at'); |
||||
|
$table->date('ended_at'); |
||||
|
$table->boolean('active')->default(true); |
||||
|
$table->timestamps(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('sprints'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateTransactionsTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('transactions', function (Blueprint $table) { |
||||
|
$table->bigInteger('id', true, true); |
||||
|
$table->bigInteger('user_id', false, true); |
||||
|
$table->bigInteger('business_id', false, true); |
||||
|
$table->integer('amount', false, true); |
||||
|
$table->boolean('succeeded')->default(false); |
||||
|
$table->json('options')->nullable(); |
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->useCurrent(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('transactions'); |
||||
|
} |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
<?php |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class CreateCostsTable extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
Schema::create('costs', function (Blueprint $table) { |
||||
|
$table->id(); |
||||
|
$table->unsignedBigInteger('business_id'); |
||||
|
$table->string('type'); |
||||
|
$table->date('month'); |
||||
|
$table->unsignedInteger('amount'); |
||||
|
$table->unsignedInteger('fee')->default(0); |
||||
|
$table->unsignedInteger('duration'); |
||||
|
$table->unsignedInteger('cost')->storedAs('fee*duration'); |
||||
|
$table->unsignedFloat('tax', 8, 2)->storedAs('(cost/100)*9'); |
||||
|
$table->json('additional')->nullable(); |
||||
|
$table->timestamp('created_at')->nullable(); |
||||
|
$table->timestamp('updated_at')->useCurrent(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::dropIfExists('costs'); |
||||
|
} |
||||
|
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue