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 |
|||
|
|||
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 |
|||
|
|||
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), |
|||
'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', |
|||
|
|||
/* |
|||
|-------------------------------------------------------------------------- |
|||
| 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 |
|||
|
|||
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' => ['*'], |
|||
|
|||
/* |
|||
* Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com` |
|||
*/ |
|||
'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' => ['*'], |
|||
|
|||
/* |
|||
* Sets the Access-Control-Expose-Headers response header with these headers. |
|||
*/ |
|||
'exposed_headers' => [], |
|||
|
|||
/* |
|||
* Sets the Access-Control-Max-Age response header when > 0. |
|||
*/ |
|||
'max_age' => 0, |
|||
|
|||
/* |
|||
* Sets the Access-Control-Allow-Credentials header. |
|||
*/ |
|||
'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 |
|||
|
|||
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 [ |
|||
|
|||
/* |
|||
|-------------------------------------------------------------------------- |
|||
| 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' => [ |
|||
'hi' => [ |
|||
'driver' => 'custom', |
|||
'via' => CreateCustomLogger::class, |
|||
'handler' => AmqpHandler::class, |
|||
'with' => [ |
|||
'exchange' => new AMQPChannel( |
|||
new AMQPStreamConnection("base-rabbitmq", 5672, 'root', 'root') |
|||
), |
|||
'exchangeName' => 'log_exchange' |
|||
], |
|||
], |
|||
|
|||
'stack' => [ |
|||
'driver' => 'stack', |
|||
'channels' => ['single'], |
|||
'channels' => ['hi', 'daily',], |
|||
'ignore_exceptions' => false, |
|||
], |
|||
|
|||
'single' => [ |
|||
'driver' => 'single', |
|||
'path' => storage_path('logs/laravel.log'), |
|||
'level' => env('LOG_LEVEL', 'debug'), |
|||
'level' => 'debug', |
|||
], |
|||
|
|||
'daily' => [ |
|||
'driver' => 'daily', |
|||
'path' => storage_path('logs/laravel.log'), |
|||
'level' => env('LOG_LEVEL', 'debug'), |
|||
'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,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 |
|||
|
|||
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 |
|||
|
|||
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