From 7acc7acee52c6c2e8a44cc9b07cda7431fc05e03 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 11:45:06 +0330 Subject: [PATCH 01/51] .idea git ignore --- .gitignore | 2 ++ .idea/codeStyles/codeStyleConfig.xml | 5 ----- .idea/liwo.iml | 8 -------- .idea/modules.xml | 8 -------- 4 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/liwo.iml delete mode 100644 .idea/modules.xml diff --git a/.gitignore b/.gitignore index 0ae59f0..0796583 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ Homestead.json Homestead.yaml npm-debug.log yarn-error.log +.idea/ + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/liwo.iml b/.idea/liwo.iml deleted file mode 100644 index c956989..0000000 --- a/.idea/liwo.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f1e8f43..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From cd5c67afafc2ccb0bb4360a5715f5aa6617deb1a Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 11:55:25 +0330 Subject: [PATCH 02/51] add docker compose --- docker-compose.yml | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..dfbf8d6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +# For more information: https://laravel.com/docs/sail +version: '3' +services: + laravel.test: + build: + context: ./vendor/laravel/sail/runtimes/8.0 + dockerfile: Dockerfile + args: + WWWGROUP: '${WWWGROUP}' + image: sail-8.0/app + ports: + - '${APP_PORT:-80}:80' + environment: + WWWUSER: '${WWWUSER}' + LARAVEL_SAIL: 1 + volumes: + - '.:/var/www/html' + networks: + - sail + depends_on: + - mysql + mysql: + image: 'mysql:8.0' + ports: + - '${FORWARD_DB_PORT:-3306}:3306' + environment: + MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' + MYSQL_DATABASE: '${DB_DATABASE}' + MYSQL_USER: '${DB_USERNAME}' + MYSQL_PASSWORD: '${DB_PASSWORD}' + MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' + volumes: + - 'sailmysql:/var/lib/mysql' + networks: + - sail + healthcheck: + test: ["CMD", "mysqladmin", "ping"] +networks: + sail: + driver: bridge +volumes: + sailmysql: + driver: local From 5ead18ce014937eecbeecfff929f3ab8f4ddd1c5 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 12:15:21 +0330 Subject: [PATCH 03/51] add phpmyadmin --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index dfbf8d6..53c8d2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,6 +35,18 @@ services: - sail healthcheck: test: ["CMD", "mysqladmin", "ping"] + myadmin: + image: 'phpmyadmin:latest' + ports: + - 8080:80 + environment: + MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' + links: + - "mysql:db" + depends_on: + - mysql + networks: + - sail networks: sail: driver: bridge From 187340281c906d498e3a521e0525ff8ac648fdd3 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 13:34:57 +0330 Subject: [PATCH 04/51] copy task, work, comment --- app/Http/Controllers/CommentController.php | 90 +++++ app/Http/Controllers/StatisticController.php | 112 +++++++ app/Http/Controllers/TaskController.php | 295 +++++++++++++++++ app/Http/Controllers/WorkController.php | 242 ++++++++++++++ app/Http/Resources/CommentResource.php | 34 ++ app/Http/Resources/TaskCollection.php | 23 ++ app/Http/Resources/TaskResource.php | 30 ++ app/Models/Comment.php | 19 ++ app/Models/Task.php | 308 ++++++++++++++++++ app/Models/Work.php | 126 +++++++ composer.json | 3 +- composer.lock | 74 ++++- .../2021_03_02_082607_create_tasks_table.php | 55 ++++ .../2021_03_02_085913_create_works_table.php | 39 +++ ...021_03_02_092444_create_comments_table.php | 36 ++ 15 files changed, 1483 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/CommentController.php create mode 100644 app/Http/Controllers/StatisticController.php create mode 100644 app/Http/Controllers/TaskController.php create mode 100644 app/Http/Controllers/WorkController.php create mode 100644 app/Http/Resources/CommentResource.php create mode 100644 app/Http/Resources/TaskCollection.php create mode 100644 app/Http/Resources/TaskResource.php create mode 100644 app/Models/Comment.php create mode 100644 app/Models/Task.php create mode 100644 app/Models/Work.php create mode 100644 database/migrations/2021_03_02_082607_create_tasks_table.php create mode 100644 database/migrations/2021_03_02_085913_create_works_table.php create mode 100644 database/migrations/2021_03_02_092444_create_comments_table.php diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php new file mode 100644 index 0000000..116bf70 --- /dev/null +++ b/app/Http/Controllers/CommentController.php @@ -0,0 +1,90 @@ + $project]); + $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail(); + if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest) + return $taskModel->assignee_id == \auth()->id() ? + Comment::where([ + ['business_id', $business], + ['project_id', $project], + ['task_id', $task], + ])->get(): + abort(Response::HTTP_FORBIDDEN); // not allowed + } else { + return Comment::where([ + ['business_id', $business], + ['project_id', $project], + ['task_id', $task], + ])->get(); + } + } + + public function store($business, $project, $task, Request $request) + { + permit('projectAccess', ['project_id' => $project]); + $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail(); + if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest) + return $taskModel->assignee_id == \auth()->id() ? + Comment::create($request->merge([ + 'business_id' => $business, + 'project_id' => $project, + 'task_id' => $task, + 'user_id' => \auth()->id(), + ])->except('_business_info')) : + abort(Response::HTTP_FORBIDDEN); // not allowed + } else { + return Comment::create($request->merge([ + 'business_id' => $business, + 'project_id' => $project, + 'task_id' => $task, + 'user_id' => \auth()->id(), + ])->except('_business_info')); + } + } + + public function show($business, $project, $task, $comment) + { + permit('projectAccess', ['project_id' => $project]); + $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail(); + if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest) + return $taskModel->assignee_id == \auth()->id() ? + Comment::findOrFail($comment) : + abort(Response::HTTP_FORBIDDEN); // not allowed + } else { + return Comment::findOrFail($comment); + } + } + + public function update($business, $project, $task, $comment, Request $request) + { + permit('projectAccess', ['project_id' => $project]); + $comment = Comment::findOrFail($comment); + if ($comment->user_id == \auth()->id()) { + $comment->update($request->except('_business_info')); + return $comment; + } + return abort(Response::HTTP_FORBIDDEN); // not allowed + } + + public function destroy($business, $project, $task, $comment) + { + permit('projectAccess', ['project_id' => $project]); + $comment = Comment::findOrFail($comment); + if ($comment->user_id == \auth()->id()) { + $comment->delete(); + return \response()->json(['message' => 'comment deleted successfully.'], Response::HTTP_OK); + } + return abort(Response::HTTP_FORBIDDEN); // not allowed + } +} diff --git a/app/Http/Controllers/StatisticController.php b/app/Http/Controllers/StatisticController.php new file mode 100644 index 0000000..572aae5 --- /dev/null +++ b/app/Http/Controllers/StatisticController.php @@ -0,0 +1,112 @@ + 0, // The sum of all tasks that are under review + 'total' => 0, // Sum of all tasks + 'overdue' => 0, // The sum of all overworked tasks + 'spent_time' => 0, // The sum of all the minutes spent on tasks + 'estimated_time' => 0, // The sum of all the minutes spent performing tasks is estimated + 'over_spent_time' => 0, // The sum of all the minutes spent overworking tasks + 'under_spent_time' => 0, // The sum of all the minutes left until estimated time + ]; + + public function index(Request $request, int $business, ?int $project = null) + { + $builder = DB::table('tasks')->where('business_id','=',$business); + if ($project) { + $builder->where('project_id', '=', $project); + } + $tasks = $builder->get(); + + $tags = DB::table('tag_task') + ->whereIn('task_id', $tasks->pluck('id')->toArray()) + ->get() + ->groupBy('task_id') + ->toArray(); + + + $result = []; + + foreach ($tasks as $task) { + + $this->addProjects($result, $task); + + $this->addSprints($result, $task); + + $this->addSystems($result, $task); + + $this->addTags($result, $task, $tags[$task->id]); + } + + return $result; + } + + + public function addProjects(&$result, $task) + { + $key = "projects.{$task->project_id}"; + + $this->subset($key, $result, $task); + } + + public function addSprints(&$result, $task) + { + $key = "projects.{$task->project_id}."; + $key .= 'sprints.'; + $key .= ($task->sprint_id ?? 0) . '.'; + $key .= ($task->assignee_id ?? 0) . '.'; + $key .= $task->workflow_id . '.'; + $key .= $task->status_id; + + $this->subset($key, $result, $task); + } + + public function addSystems(&$result, $task) + { + $key = "projects.{$task->project_id}."; + $key .= 'systems.'; + $key .= ($task->system_id ?? 0) . '.'; + $key .= ($task->assignee_id ?? 0) . '.'; + $key .= $task->workflow_id . '.'; + $key .= $task->status_id; + + $this->subset($key, $result, $task); + } + + public function addTags(&$result, $task, $tags) + { + foreach ($tags as $tag) { + $key = "projects.{$task->project_id}."; + $key .= 'tags.'; + $key .= $tag->id . '.'; + $key .= ($task->assignee_id ?? 0) . '.'; + $key .= $task->workflow_id . '.'; + $key .= $task->status_id; + + $this->subset($key, $result, $task); + } + } + + public function subset($key , &$result, $task) + { + $node = Arr::get($result, $key, $this->map); + + $node['test'] += $task->ready_to_test; + $node['total'] += 1; + $node['overdue'] += !$task->on_time; + $node['spent_time'] += $task->spent_time; + $node['estimated_time'] += $task->estimated_time; + $node['over_spent_time'] += $task->estimated_time < $task->spent_time ? $task->spent_time - $task->estimated_time : 0; + $node['under_spent_time'] += $task->estimated_time > $task->spent_time ? $task->estimated_time - $task->spent_time : 0; + + Arr::set($result, $key, $node); + } +} diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php new file mode 100644 index 0000000..f447782 --- /dev/null +++ b/app/Http/Controllers/TaskController.php @@ -0,0 +1,295 @@ +limit > 100 ? 10 : $request->limit; + $this->indexValidation($request); + $tasks = $this->indexFiltering($business) + ->when($request->filled('group'), function ($q) use ($request) { + return $q->report($request->group); + }); + + return $request->filled('group') ? + $tasks->get()->groupBy($request->group) + ->map(function ($q) { return $q->keyBy('status_id'); }) + : /*response()->json(collect(['now' => Carbon::now()->toDateString()])->merge(*/new TaskCollection($tasks->paginate($per_page));//)); + } + + public function indexValidation($request) + { + $bound = 10; + $this->validate($request, [ + 'filter.project_id' => [new maxBound($bound)] , + 'filter.creator_id' => [new maxBound($bound)] , + 'filter.assignee_id' => [new maxBound($bound)] , + 'filter.system_id' => [new maxBound($bound)] , + 'filter.workflow_id' => [new maxBound($bound)] , + 'filter.status_id' => [new maxBound($bound)] , + 'filter.approver_id' => [new maxBound($bound)] , + 'filter.priority_min' => 'nullable|numeric|between:1,10' , + 'filter.priority_max' => 'nullable|numeric|between:1,10' , + 'filter.ready_to_test' => 'nullable|boolean' , + 'filter.on_time' => 'nullable|boolean' , + ]); + } + + public function indexFiltering($business) + { + $query = Task::where('business_id', $business); + $taskQ = QueryBuilder::for($query) + ->with('tagTask') + ->select(DB::raw('tasks.* , (spent_time - estimated_time) as over_spent')) + ->allowedFilters([ + AllowedFilter::exact('project_id'), + AllowedFilter::exact('system_id'), + AllowedFilter::exact('creator_id'), + AllowedFilter::exact('assignee_id'), + AllowedFilter::exact('approver_id'), + AllowedFilter::exact('sprint_id'), + AllowedFilter::exact('workflow_id'), + AllowedFilter::exact('status_id'), + AllowedFilter::exact('on_time'), + AllowedFilter::exact('ready_to_test'), + AllowedFilter::exact('tagTask.tag_id'), + AllowedFilter::scope('priority_min'), + AllowedFilter::scope('priority_max'), + AllowedFilter::scope('creates_before'), + AllowedFilter::scope('creates_after'), + AllowedFilter::scope('creates_in'), + AllowedFilter::scope('updates_before'), + AllowedFilter::scope('updates_after'), + AllowedFilter::scope('updates_in'), + AllowedFilter::scope('spent_from'), + AllowedFilter::scope('spent_to'), + AllowedFilter::scope('estimated_from'), + AllowedFilter::scope('estimated_to'), + AllowedFilter::scope('starts_before'), + AllowedFilter::scope('starts_after'), + AllowedFilter::scope('starts_in'), + AllowedFilter::scope('finish_before'), + AllowedFilter::scope('finish_after'), + AllowedFilter::scope('finish_in'), + AllowedFilter::scope('completes_before'), + AllowedFilter::scope('completes_after'), + AllowedFilter::scope('completes_in'), + AllowedFilter::scope('over_spent_from'), + AllowedFilter::scope('over_spent_to'), + AllowedFilter::scope('due_date_before'), + AllowedFilter::scope('due_date_after'), + AllowedFilter::scope('due_date_in'), + AllowedFilter::scope('my_watching'), + AllowedFilter::scope('over_due'), + ]); + 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); + $taskQ->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('assignee_id', auth()->id()); + }); + }); + } + return $taskQ; + } + + 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($business, $project, Request $request) + { + permit('projectTasks', ['project_id' => $project]); + + $task = Task::create($request->merge( + ['business_id' => $business, 'project_id' => $project, 'creator_id' => \auth()->id()] + )->except(['_business_info', 'completed_at', 'on_time', 'ready_to_test'])); + + + return new TaskResource($task); + } + + public function storeTagTasks($tags, $task) { + $tagModels = []; + if (isset($tags)) { + foreach ($tags as $tag) { + array_push($tagModels, + new TagTask(['tag_id' => $tag, 'task_id' => $task->id]) + ); + } + $task->tags()->saveMany($tagModels); + } + } + + /** + * Rule's: + * 1) guest's only can see self task + * 2) user is active in project + */ + public function show($business, $task, $project =null) + { + $task = Task::findOrFail($task); + $project = $task->project_id; + permit('projectAccess', ['project_id' => $project]); + if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest) + return $task->assignee_id == \auth()->id() ? + new TaskResource($task) : + abort(Response::HTTP_METHOD_NOT_ALLOWED); // not allowed + } else { + return new TaskResource($task); + } + } + + /** + * Rule's: + * 1) update assignee_id when not exist work time and active user + * 2) update approver_id when atLeast colleague + * 3) update ready_to_test when assignee_id == auth xor assignee_id == null and isAdmin + * 4) update tags + * 5) update completed_at when status in [done, close] + * 6) due_date before sprint end_time + */ + public function update($business, $project, $task, Request $request) + { + permit('isProjectGuest', ['project_id' => $project]); + $taskModel = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail(); + + if ($taskModel->assignee_id != \auth()->id() && can('isDefiniteGuestInProject', ['project_id' => $project])) { + permit('isDefiniteGuestInProject', ['project_id' => $project]); + } + + if($request->filled('watchers')) { + $watchers = $taskModel->watchers ?? []; + if(!can('isDefiniteGuestInProject', ['project_id' => $project]) && !can('projectTasks', ['project_id' => $project])) { + if (array_search(auth()->id(), $watchers) !== false) { + // remove auth from watchers + $watchers = array_values(\array_diff($watchers, [auth()->id()])); + } else { + // add auth to watchers + $watchers = array_merge($watchers, [auth()->id()]); + } + } + if(can('projectTasks', ['project_id' => $project])) { + $watchers = array_intersect($watchers ?? [], $request->watchers); + } + $request->merge(['watchers' => $watchers]); + } + + if (!can('projectTasks', ['project_id' => $project])) { + $request->replace($request->only(['_business_info', 'ready_to_test', 'status_id', 'watchers'])); + } + if ($taskModel->assignee_id != \auth()->id()) { + $request->request->remove('ready_to_test'); + } + + + if (isset(\request('_business_info')['workflows'][$request->workflow_id]['statuses'][$request->status_id]['state'])) { + $state = \request('_business_info')['workflows'][$request->workflow_id]['statuses'][$request->status_id]['state']; + if ($state == enum('status.states.close.id') || $state == enum('status.states.done.id')) { + //ToDo: is needed to check before state is done or close? + $request->merge([ + 'completed_at' => Carbon::now(), + 'work_finish' => Work::where('task_id', $task)->latest()->first()->ended_at ?? null + ]); + if (isset($taskModel->due_date) && $taskModel->due_date < date('yy-m-d')) { + //check if before set due date and miss, we change on time flag + $request->merge(['on_time' => false]); + } + } + } + if (!$request->has('tags')) { + $request->merge(['tags' => []]); + } + $taskModel->update($request->except('_business_info')); + return new TaskResource($taskModel); + } + + public function updateReadyToTest($business, $project, $task) + { + permit('isProjectGuest', ['project_id' => $project]); + $task = Task::where([['project_id', $project ], ['id', $task]])->firstOrFail(); + if ($task->assignee_id == \auth()->id()) { + $task->update([ + 'ready_to_test' => 1 + ]); + } else { + return abort(Response::HTTP_FORBIDDEN); // not allowed + } + return $task->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']); + } + + /** + * Rule's: + * 1) delete only not work time (soft delete) + */ + public function destroy($task) + { + $taskModel = Task::findOrFail($task); + if (Work::where('task_id', $task)->exists()) { + return \response()->json(['task_id' => 'The task id cannot be deleted.'], Response::HTTP_UNPROCESSABLE_ENTITY); + } + $taskModel->delete(); + return \response()->json(['message' => 'task deleted successfully.'], Response::HTTP_OK); + } + + public function toggleWatcher($business, $task, $project =null) + { + permit('isProjectGuest', ['project_id' => $project]); + $taskModel = Task::findOrFail($task); + $watchers = $taskModel->watchers ?? []; + + if (array_search(auth()->id(), $watchers) !== false) { + // remove auth from watchers + $new_watchers = array_values(\array_diff($watchers, [auth()->id()])); + } else { + // add auth to watchers + $new_watchers = array_merge($watchers, [auth()->id()]); + } + + $taskModel->update([ + 'watchers' => $new_watchers + ]); + return new TaskResource($taskModel); + } +} diff --git a/app/Http/Controllers/WorkController.php b/app/Http/Controllers/WorkController.php new file mode 100644 index 0000000..a2fa6ba --- /dev/null +++ b/app/Http/Controllers/WorkController.php @@ -0,0 +1,242 @@ +limit > 100 ? 10 : $request->limit; + $workQ = $this->indexFiltering($business) + ->when($request->filled('group'), function ($q) use ($request) { + return $request->group == 'user' ? $q->report() : $q->reportByDate(); + }); + + return $request->filled('group') ? $workQ->get() : + $workQ->defaultSort('-id') + ->allowedSorts('id', 'started_at')->paginate($per_page); + } + + public function indexFiltering($business) + { + $query = Work::where('works.business_id', $business); + $workQ = queryBuilder::for($query) + ->join('tasks', 'tasks.id', 'works.task_id') + ->select('works.*', 'tasks.title', 'tasks.sprint_id', 'tasks.system_id') + ->allowedFilters([ + AllowedFilter::exact('project_id'), + AllowedFilter::exact('tasks.sprint_id', null, false), + AllowedFilter::exact('tasks.system_id', null, false), + AllowedFilter::exact('user_id'), + AllowedFilter::scope('started_at_in'), + AllowedFilter::scope('started_at'), + AllowedFilter::scope('spent_time_from'), + AllowedFilter::scope('spent_time_to'), + ]); + 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); + $workQ->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('assignee_id', auth()->id()); + }); + }); + } + return $workQ; + } + + 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 show($business, $project, $task, $work) + { + permit('projectAccess', ['project_id' => $project]); + $work = Work::where([['project_id', $project ], ['task_id', $task], ['id', $work]])->firstOrFail(); + if (can('isDefiniteGuestInProject', ['project_id' => $project])){ // is guest in project (only guest) + return $work->user_id == \auth()->id() ? $work : abort(Response::HTTP_FORBIDDEN); // not allowed + } else { + return $work; + } + } + /** + * Rule's: + * 1) only assignee_id can store work + * 2) started_at after task created_at + * 3) ended_at after started_at + * 4) not any work before in this work + */ + public function store($business, $project, $task, Request $request) + { + $taskModel = Task::findOrFail($task); + if ($taskModel->assignee_id != auth()->id()) { + abort(Response::HTTP_FORBIDDEN); // not allowed + } + + $end = Carbon::createFromFormat('Y-m-d H:i', $request->ended_at); + $start = Carbon::createFromFormat('Y-m-d H:i', $request->started_at); + $diff_in_min = $end->diffInMinutes($start); + $work = Work::create($request->merge([ + 'business_id' => $business, + 'project_id' => $project, + 'task_id' => $task, + 'user_id' => auth()->id(), + 'minute_sum' => $diff_in_min, + 'task' => [ + 'spent_time' => $taskModel->spent_time + $diff_in_min + ] + ])->except('_business_info')); + $taskModel->refresh(); +// $taskModel->update([ +// 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, +// 'spent_time' => $taskModel->spent_time + $diff_in_min +// ]); + return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']); + } + + public function storeValidation($request, $taskModel) + { + $this->validate($request, [ + 'message' => 'nullable|string|min:3|max:225', + 'started_at' => 'required|date_format:Y-m-d H:i|after:'.$taskModel->created_at, + 'ended_at' => 'required|date_format:Y-m-d H:i|after:started_at', + ]); + $state = \request('_business_info')['workflows'][$taskModel->workflow_id]['statuses'][$taskModel->status_id]['state'] ?? null; + if ($state == enum('status.states.close.id') || $state == enum('status.states.done.id')) { + throw ValidationException::withMessages(['task' => 'The selected task is invalid.']); + } + $works = Work::where([ + ['ended_at', '>', $request->started_at], + ['ended_at', '<', $request->ended_at], + ])->orWhere([ + ['started_at', '>', $request->started_at], + ['started_at', '<', $request->ended_at], + ])->orWhere([ + ['started_at', '>=', $request->started_at], + ['ended_at', '<=', $request->ended_at], + ])->exists(); + if ($works) { + throw ValidationException::withMessages(['work' => 'The selected work is invalid.']); + } + } + + /** + * Rule's: + * 1) only assignee_id can store work + * 2) started_at after task created_at + * 3) ended_at after started_at + * 4) not any work before in this work + */ + public function update($business, $project, $task, $work, Request $request) + { + $taskModel = Task::findOrFail($task); + $workModel = Work::findOrFail($work); + if ($taskModel->assignee_id != auth()->id()) { + abort(Response::HTTP_FORBIDDEN); // not allowed + } + + $end = Carbon::createFromFormat('Y-m-d H:i', $request->ended_at); + $start = Carbon::createFromFormat('Y-m-d H:i', $request->started_at); + $new_diff_in_min = $end->diffInMinutes($start); + $old_diff_in_min = $workModel->minute_sum; + $workModel->update($request->merge([ + 'business_id' => $business, + 'project_id' => $project, + 'task_id' => $task, + 'user_id' => auth()->id(), + 'minute_sum' => $new_diff_in_min, + 'task' => [ + 'spent_time' => ($taskModel->spent_time - $old_diff_in_min) + $new_diff_in_min + ] + ])->except('_business_info')); + $taskModel->refresh(); +// $taskModel->update([ +// 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, +// 'spent_time' => ($taskModel->spent_time - $old_diff_in_min) + $new_diff_in_min +// ]); + return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']); + } + + public function updateValidation($request, $taskModel, $workModel) + { + $this->validate($request, [ + 'message' => 'nullable|string|min:3|max:225', + 'started_at' => 'nullable|date_format:Y-m-d H:i|after:'.$taskModel->created_at, + 'ended_at' => 'nullable|date_format:Y-m-d H:i|after:started_at', + ]); + //ToDo: is needed to check status is active or idea?? + $works = false; + if ($request->filled('started_at') || $request->filled('ended_at')) { + $started_at = $request->started_at ?? $workModel->started_at->format('Y-m-d H:i'); + $ended_at = $request->ended_at ?? $workModel->ended_at->format('Y-m-d H:i'); + if (strtotime($ended_at) <= strtotime($started_at)) { + throw ValidationException::withMessages(['ended_at' => 'The ended at must be a date after started at.']); + } + $works = Work::where([ + ['ended_at', '>', $started_at], + ['ended_at', '<', $ended_at], + ])->orWhere([ + ['started_at', '>', $started_at], + ['started_at', '<', $ended_at], + ])->orWhere([ + ['started_at', '>=', $started_at], + ['ended_at', '<=', $ended_at], + ])->where('id', '!=', $workModel->id)->exists(); + $end = Carbon::createFromFormat('Y-m-d H:i', $ended_at); + $start = Carbon::createFromFormat('Y-m-d H:i', $started_at); + \request()->merge(['minute_sum' => $end->diffInMinutes($start)]); + } + if ($works) { + throw ValidationException::withMessages(['work' => 'The selected work is invalid.']); + } + } + + public function destroy($business, $project, $task, $work) + { + $taskModel = Task::findOrFail($task); + $workModel = Work::findOrFail($work); + if ($taskModel->assignee_id != auth()->id()) { + abort(Response::HTTP_FORBIDDEN); // not allowed + } + $diff_in_min = $workModel->minute_sum; + $workModel->delete(); + $taskModel->update([ + 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, + 'spent_time' => ($taskModel->spent_time - $diff_in_min) + ]); + return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works','comments']); + } +} diff --git a/app/Http/Resources/CommentResource.php b/app/Http/Resources/CommentResource.php new file mode 100644 index 0000000..eecc44a --- /dev/null +++ b/app/Http/Resources/CommentResource.php @@ -0,0 +1,34 @@ + 'task', + '_resource' => 'comment', + ]; + + foreach ($this->getAttributes() as $attribute => $value) { + switch ($attribute) { + case 'task_id' : + case 'user_id' : + case 'body' : + $resource[$attribute] = $value; + break; + } + } + + return $resource; + } +} diff --git a/app/Http/Resources/TaskCollection.php b/app/Http/Resources/TaskCollection.php new file mode 100644 index 0000000..be5944b --- /dev/null +++ b/app/Http/Resources/TaskCollection.php @@ -0,0 +1,23 @@ + TaskResource::collection($this->collection), + 'now' => Carbon::now()->toDateString() + ]; + } +} diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php new file mode 100644 index 0000000..6737a5a --- /dev/null +++ b/app/Http/Resources/TaskResource.php @@ -0,0 +1,30 @@ +getAttributes() as $attribute => $value) { + $resource[$attribute] = $value; + if ($attribute == 'watchers') { + $resource[$attribute] = json_decode($value); + } + } + + $resource['tags'] = $this->tagTask()->pluck('tag_id')->toArray(); + $resource['works'] = $this->works; + $resource['comments'] = $this->comments; + + return $resource; + } +} diff --git a/app/Models/Comment.php b/app/Models/Comment.php new file mode 100644 index 0000000..b392b45 --- /dev/null +++ b/app/Models/Comment.php @@ -0,0 +1,19 @@ + 'required|string|min:3|max:1000', + ]; + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..b08e588 --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,308 @@ + 'tag_task'] + ]; + protected $fillable_relations = [ + 'tags' + ]; + + protected $casts = [ + 'work_start' => 'datetime:Y-m-d H:i', + 'work_finish' => 'datetime:Y-m-d H:i', + 'watchers' => 'array', + ]; + + public function rules() + { + $validations = [ + 'assignee_id' => ['nullable', 'numeric', + function ($attribute, $value, $fail) { + //check assignee at least guest in project + if (!can('isProjectGuest', ['project_id' => request()->route('project'), 'user_id' => $value])) { + $fail('The selected '.$attribute.' is invalid.'); + } + if (request()->method() === 'PUT' && ($this->assignee_id != $value && Work::where('task_id', $this->id)->exists())) { + //when update execute + //check if change assignee then should be on task not work set + $fail('The selected '.$attribute.' is invalid.'); + } + }], + 'system_id' => ['nullable', 'numeric', + Rule::in(\request('_business_info')['info']['projects'][request()->route('project')]['systems'])], + 'sprint_id' => ['nullable', 'numeric', + Rule::in(\request('_business_info')['info']['projects'][request()->route('project')]['sprints'])], + 'workflow_id' => ['required', 'numeric', + Rule::in(array_keys(\request('_business_info')['info']['workflows']))], + 'status_id' => 'required|numeric', + 'approver_id' => ['nullable', 'numeric', + function ($attribute, $value, $fail) { + //check approval at least colleague in project + if (!can('isProjectColleague', ['project_id' => request()->route('project'), 'user_id' => $value])) { + $fail('The selected '.$attribute.' is invalid.'); + } + }], + 'title' => 'required|string|min:3|max:254', + 'description' => 'nullable|string|min:2|max:1000', + 'priority' => 'nullable|numeric|between:1,10', + 'estimated_time' => 'nullable|numeric|min:30', + 'due_date' => 'bail|nullable|date|date_format:Y-m-d|after_or_equal:'. + ((request()->method() === 'POST') ? date('yy-m-d') : $this->created_at->toDateString()), + 'tags' => 'nullable|array', + 'tags.*' => [new RequiredIf(isset($request->tags)), 'numeric', + Rule::in(\request('_business_info')['info']['tags'])], + ] ; + $workflow_id = $this->workflow_id; + if (request()->filled('workflow_id') && isset(request('_business_info')['info']['workflows'][request()->workflow_id])) { + $workflow_id = request()->workflow_id; + } + if (isset($workflow_id)) { + $validations['status_id'] = ['bail', 'required', 'numeric', + //check status exists in status list + Rule::in(\request('_business_info')['info']['workflows'][$workflow_id]['statuses']), + function ($attribute, $value, $fail) use ($workflow_id) { + //check not close or done + $state = \request('_business_info')['workflows'][$workflow_id]['statuses'][$value]['state']; + if (request()->method() == 'POST' && ($state == enum('status.states.close.id') || $state == enum('status.states.done.id'))) { + $fail('The selected '.$attribute.' is invalid.'); + } + if ((request()->method() == 'PUT') && !can('projectTasks', ['project_id' => request()->route('project')]) + && $state != enum('status.states.active.id')) { + $fail('The selected '.$attribute.' is invalid.'); + } + }]; + } + + return $validations; + } + public function updateRelations() + { + // tags relations + $this->dirties['tags'] = $this->tags()->sync($this->filled_relations['tags']); + + //old code +// if (!empty($this->filled_relations['tags'])) { +// $this->dirties['tags'] = $this->tags()->sync($this->filled_relations['tags']); +// } + + } + + public function getValueOf(?string $key) + { + $values = [ + 'business_id' => $this->business_id, + 'project_id' => $this->project_id, + 'sprint_id' => $this->sprint_id, + 'workflow_id' => $this->workflow_id, + 'status_id' => $this->status_id, + 'system_id' => $this->system_id, + 'task_id' => $this->id, + 'subject_id' => $this->id, + 'user_id' => $this->assignee_id, + ]; + + if ($key && isset($values, $key)) { + return $values[$key]; + } + + return $values; + } + + public function business() + { + return $this->belongsTo(Business::class, 'business_id', 'id', __FUNCTION__); + } + + public function creator() + { + return $this->belongsTo(User::class, 'creator_id', 'id', __FUNCTION__); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id', 'id', __FUNCTION__); + } + + public function project() + { + return $this->belongsTo(Project::class, 'project_id', 'id', __FUNCTION__); + } + + public function tags() + { + return $this->belongsToMany( + Tag::class, 'tag_task', 'task_id', 'tag_id', + 'id', 'id', __FUNCTION__ + )->using(ReportableRelation::class); + } + + public function tagTask() + { + return $this->hasMany(TagTask::class, 'task_id', 'id'); + } + + public function works() + { + return $this->hasMany(Work::class, 'task_id', 'id'); + } + + public function comments() + { + return $this->hasMany(Comment::class, 'task_id', 'id'); + } + + public function scopePriorityMin($query, $min) + { + return $query->where('tasks.priority', '>=', $min); + } + public function scopePriorityMax($query, $max) + { + return $query->where('tasks.priority', '<=', $max); + } + public function scopeCreatesBefore($query, $date) + { + //ToDo: comment lines have better performance but should be test +// return $query->whereDate('tasks.created_at', '<=', Carbon::parse($date.' 23:59:59')); +// return $query->where('tasks.created_at', '<', (new DateTime('2014-07-10'))->modify('+1 day')->format('Y-m-d')); + return $query->whereDate('tasks.created_at', '<=', Carbon::parse($date)); + } + public function scopeCreatesAfter($query, $date) + { + return $query->whereDate('tasks.created_at', '>=', Carbon::parse($date)); + } + public function scopeCreatesIn($query, $days) + { + return $query->whereDate('tasks.created_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeUpdatesBefore($query, $date) + { + return $query->whereDate('tasks.updated_at', '<=', Carbon::parse($date)); + } + public function scopeUpdatesAfter($query, $date) + { + return $query->whereDate('tasks.updated_at', '>=', Carbon::parse($date)); + } + public function scopeUpdatesIn($query, $days) + { + return $query->whereDate('tasks.updated_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeSpentFrom($query, $minute) + { + return $query->where('tasks.spent_time', '>=', $minute); + } + public function scopeSpentTo($query, $minute) + { + return $query->where('tasks.spent_time', '<=', $minute); + } + public function scopeEstimatedFrom($query, $minute) + { + return $query->where('tasks.estimated_time', '>=', $minute); + } + public function scopeEstimatedTo($query, $minute) + { + return $query->where('tasks.estimated_time', '<=', $minute); + } + public function scopeStartsBefore($query, $date) + { + return $query->whereDate('tasks.work_start', '<=', Carbon::parse($date)); + } + public function scopeStartsAfter($query, $date) + { + return $query->whereDate('tasks.work_start', '>=', Carbon::parse($date)); + } + public function scopeStartsIn($query, $days) + { + return $query->whereDate('tasks.work_start', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeFinishBefore($query, $date) + { + return $query->whereDate('tasks.work_finish', '<=', Carbon::parse($date)); + } + public function scopeFinishAfter($query, $date) + { + return $query->whereDate('tasks.work_finish', '>=', Carbon::parse($date)); + } + public function scopeFinishIn($query, $days) + { + return $query->whereDate('tasks.work_finish', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeCompletesBefore($query, $date) + { + return $query->where('tasks.completed_at', '<=', $date); + } + public function scopeCompletesAfter($query, $date) + { + return $query->where('tasks.completed_at', '>=', $date); + } + public function scopeCompletesIn($query, $days) + { + return $query->whereDate('tasks.completed_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeDueDateBefore($query, $date) + { + return $query->where('tasks.due_date', '<=', $date); + } + public function scopeDueDateAfter($query, $date) + { + return $query->where('tasks.due_date', '>=', $date); + } + public function scopeDueDateIn($query, $days) + { + return $query->whereDate('tasks.due_date', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + public function scopeOverSpentFrom($query, $minute) + { + return $query->whereColumn('spent_time', '>', 'estimated_time') + ->having('over_spent', '>=', $minute); + } + public function scopeOverSpentTo($query, $minute) + { + return $query->whereColumn('spent_time', '>', 'estimated_time') + ->having('over_spent', '<=', $minute); + } + public function scopeMyWatching($query) + { + return $query->whereJsonContains('watchers', auth()->id()); + } + public function scopeOverDue($query) + { + return $query->whereColumn('due_date', '<', 'completed_at'); + } + public function scopeReport($query, $group_field) + { + return $query->select(DB::raw($group_field.' , status_id, COUNT(*) as total, SUM(spent_time) as spent_sum, + SUM(estimated_time) as estimated_sum,SUM(ready_to_test) as test_sum, + COUNT(completed_at) as completed_total, SUM(on_time) as on_time_total, + SUM(Case When spent_time > estimated_time Then (spent_time - estimated_time) Else 0 End) as over_spent_sum, + SUM(Case When (spent_time <= estimated_time) And (completed_at) Then (estimated_time - spent_time) Else 0 End) + as under_spent_sum')) + ->groupBy($group_field, 'status_id'); + } +} diff --git a/app/Models/Work.php b/app/Models/Work.php new file mode 100644 index 0000000..b378737 --- /dev/null +++ b/app/Models/Work.php @@ -0,0 +1,126 @@ + 'datetime:Y-m-d H:i', + 'ended_at' => 'datetime:Y-m-d H:i' + ]; + + protected $reportable = [ + 'message', 'minute_sum', 'started_at', 'ended_at', + ]; + + public function rules() + { + $started_at = request()->started_at ?? $this->started_at->format('Y-m-d H:i'); + $ended_at = request()->ended_at ?? $this->ended_at->format('Y-m-d H:i'); + return [ + 'message' => 'nullable|string|min:3|max:225', + 'started_at' => 'required|date_format:Y-m-d H:i|after:'.$this->task()->first()->created_at, + 'ended_at' => [ + 'required', 'date_format:Y-m-d H:i', 'after:'.$started_at, + function ($attribute, $value, $fail) use ($ended_at, $started_at) { + $works = \App\Work::where([ + ['ended_at', '>', $started_at], + ['ended_at', '<', $ended_at], + ])->orWhere([ + ['started_at', '>', $started_at], + ['started_at', '<', $ended_at], + ])->orWhere([ + ['started_at', '>=', $started_at], + ['ended_at', '<=', $ended_at], + ])->when(isset($this->id), function ($query) { + return $query->where('id', '!=', $this->id); + })->exists(); + if ($works) { + $fail('The selected work is invalid.'); + } + } + ], + ]; + + } + + 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' => null, + 'task_id' => $this->task->id, + 'subject_id' => $this->id, + 'user_id' => null, + ]; + + if ($key && isset($values, $key)) { + return $values[$key]; + } + + return $values; + } + + public function updateRelations() + { + $this->filled_relations['task']['work_start'] = Work::where('task_id', $this->task_id)->orderBy('started_at')->first()->started_at ?? null; + $this->task()->update($this->filled_relations['task']); + } + + public function task() + { + return $this->belongsTo(Task::class, 'task_id', 'id'); + } + + public function scopeStartedAtIn($query, $days) + { + return $query->whereDate('started_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()); + } + + public function scopeStartedAt($query, $date) + { + return $query->whereDate('started_at', '=', $date); + } + + public function scopeSpentTimeFrom($query, $data) + { + return $query->where('minute_sum', '>=', $data); + } + + public function scopeSpentTimeTo($query, $data) + { + return $query->where('minute_sum', '<=', $data); + } + + public function scopeReport($query) + { + return $query->select( + DB::raw('user_id , MIN(started_at) as started_at_min, MAX(ended_at) as ended_at_max, SUM(minute_sum) as work_minute_sum') + )->groupBy('user_id'); + } + + public function scopeReportByDate($query) + { + return $query->select( + DB::raw('DATE(started_at) as date, SUM(minute_sum) as work_minute_sum') + )->groupBy('date'); + } +} diff --git a/composer.json b/composer.json index 3795a6d..c47bcbc 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", "laravel/framework": "^8.12", - "laravel/tinker": "^2.5" + "laravel/tinker": "^2.5", + "spatie/laravel-query-builder": "^3.3" }, "require-dev": { "facade/ignition": "^2.5", diff --git a/composer.lock b/composer.lock index a5ae24e..06cb69e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4a16c3c541bd99241cab1c21ce8e83ac", + "content-hash": "727372edb248a9d83eed7a324b8eda99", "packages": [ { "name": "asm89/stack-cors", @@ -2226,6 +2226,76 @@ ], "time": "2020-08-18T17:17:46+00:00" }, + { + "name": "spatie/laravel-query-builder", + "version": "3.3.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-query-builder.git", + "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", + "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", + "shasum": "" + }, + "require": { + "illuminate/database": "^6.0|^7.0|^8.0", + "illuminate/http": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "php": "^7.3|^8.0" + }, + "require-dev": { + "ext-json": "*", + "laravel/legacy-factories": "^1.0.4", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^4.9|^5.8|^6.3", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\QueryBuilder\\QueryBuilderServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\QueryBuilder\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Easily build Eloquent queries from API requests", + "homepage": "https://github.com/spatie/laravel-query-builder", + "keywords": [ + "laravel-query-builder", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-query-builder/issues", + "source": "https://github.com/spatie/laravel-query-builder" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + } + ], + "time": "2020-11-26T14:51:30+00:00" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.2.5", @@ -6968,5 +7038,5 @@ "php": "^7.3|^8.0" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } diff --git a/database/migrations/2021_03_02_082607_create_tasks_table.php b/database/migrations/2021_03_02_082607_create_tasks_table.php new file mode 100644 index 0000000..0ce9c7f --- /dev/null +++ b/database/migrations/2021_03_02_082607_create_tasks_table.php @@ -0,0 +1,55 @@ +id(); + $table->unsignedBigInteger('business_id'); + $table->unsignedBigInteger('project_id'); + $table->unsignedBigInteger('creator_id'); + $table->unsignedBigInteger('assignee_id')->nullable(); + $table->unsignedBigInteger('system_id')->nullable(); + $table->unsignedBigInteger('sprint_id')->nullable(); + $table->unsignedBigInteger('workflow_id'); + $table->unsignedBigInteger('status_id'); + $table->unsignedBigInteger('approver_id')->nullable(); + + $table->string('title'); + $table->text('description')->nullable(); + $table->integer('priority')->default(1); + $table->boolean('on_time')->default(true); + $table->boolean('ready_to_test')->default(false); + + $table->json('watchers')->nullable(); + + $table->integer('spent_time')->default(0); + $table->integer('estimated_time')->default(0); + $table->date('due_date')->nullable(); + $table->date('completed_at')->nullable(); + $table->timestamp('work_start')->nullable(); + $table->timestamp('work_finish')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('tasks'); + } +} diff --git a/database/migrations/2021_03_02_085913_create_works_table.php b/database/migrations/2021_03_02_085913_create_works_table.php new file mode 100644 index 0000000..4274ff8 --- /dev/null +++ b/database/migrations/2021_03_02_085913_create_works_table.php @@ -0,0 +1,39 @@ +id(); + $table->unsignedBigInteger('business_id'); + $table->unsignedBigInteger('project_id'); + $table->unsignedBigInteger('task_id'); + $table->unsignedBigInteger('user_id'); + $table->string('message')->nullable(); + $table->integer('minute_sum')->default(0); + $table->dateTime('started_at'); + $table->dateTime('ended_at'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('works'); + } +} diff --git a/database/migrations/2021_03_02_092444_create_comments_table.php b/database/migrations/2021_03_02_092444_create_comments_table.php new file mode 100644 index 0000000..c653af3 --- /dev/null +++ b/database/migrations/2021_03_02_092444_create_comments_table.php @@ -0,0 +1,36 @@ +id(); + $table->unsignedBigInteger('business_id'); + $table->unsignedBigInteger('project_id'); + $table->unsignedBigInteger('task_id'); + $table->unsignedBigInteger('user_id'); + $table->text('body'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('comments'); + } +} From 9ff6e84f836dd0e56e55d6a226983ec01467ef06 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Tue, 2 Mar 2021 13:40:01 +0330 Subject: [PATCH 05/51] WooooHooooo, Hello again my beloved LARAVEL --- .env.example | 6 +- app/Console/Commands/CostCommand.php | 136 + app/Http/Controllers/AuthController.php | 268 ++ app/Http/Controllers/BusinessController.php | 195 + app/Http/Controllers/CreditController.php | 69 + app/Http/Controllers/FileController.php | 203 + app/Http/Controllers/InvoiceController.php | 80 + app/Http/Controllers/ProjectController.php | 159 + app/Http/Controllers/SprintController.php | 47 + app/Http/Controllers/StatusController.php | 37 + app/Http/Controllers/SystemController.php | 40 + app/Http/Controllers/TagController.php | 37 + app/Http/Controllers/TaskFileController.php | 89 + app/Http/Controllers/UserController.php | 69 + app/Http/Controllers/WorkflowController.php | 71 + app/Http/Resources/BusinessResource.php | 34 + app/Http/Resources/FileResource.php | 37 + app/Http/Resources/FingerprintResource.php | 34 + app/Http/Resources/ProjectResource.php | 38 + app/Http/Resources/TransactionResource.php | 32 + app/Http/Resources/UserResource.php | 34 + app/Models/Activity.php | 10 + app/Models/Business.php | 486 ++ app/Models/Cost.php | 24 + app/Models/File.php | 75 + app/Models/Fingerprint.php | 30 + app/Models/Project.php | 204 + app/Models/SoftDeletes.php | 29 + app/Models/Sprint.php | 73 + app/Models/Status.php | 55 + app/Models/System.php | 69 + app/Models/Tag.php | 58 + app/Models/Task.php | 25 + app/Models/Transaction.php | 164 + app/Models/User.php | 207 +- app/Models/Workflow.php | 88 + app/Providers/AuthServiceProvider.php | 21 +- app/Rules/MaxBound.php | 40 + app/Scopes/BusinessScope.php | 18 + .../Avatar/DefaultConversionFileNamer.php | 15 + app/Utilities/Avatar/DefaultPathGenerator.php | 41 + app/Utilities/BusinessInfoRequestMixin.php | 43 + app/Utilities/Payload.php | 83 + app/Utilities/RequestMixin.php | 51 + .../Zarinpal/Drivers/DriverInterface.php | 46 + app/Utilities/Zarinpal/Drivers/RestDriver.php | 197 + .../Zarinpal/Laravel/Facade/Zarinpal.php | 24 + .../Laravel/ZarinpalServiceProvider.php | 46 + app/Utilities/Zarinpal/Zarinpal.php | 130 + composer.json | 15 + composer.lock | 4094 +++++++++++++---- config.default/app.php | 232 + {config => config.default}/auth.php | 0 {config => config.default}/broadcasting.php | 0 {config => config.default}/cache.php | 0 config.default/cors.php | 34 + {config => config.default}/database.php | 0 config.default/filesystems.php | 72 + {config => config.default}/hashing.php | 0 config.default/logging.php | 104 + {config => config.default}/mail.php | 0 {config => config.default}/queue.php | 0 config.default/services.php | 33 + {config => config.default}/session.php | 0 {config => config.default}/view.php | 0 config/amqp.php | 73 + config/app.php | 221 +- config/cors.php | 43 +- config/filesystems.php | 21 +- config/geoip.php | 172 + config/logging.php | 98 +- config/media-library.php | 179 + config/query-builder.php | 38 + config/services.php | 40 +- database/factories/BusinessFactory.php | 19 + database/factories/CostFactory.php | 19 + database/factories/FileFactory.php | 35 + database/factories/FingerprintFactory.php | 38 + database/factories/ProjectFactory.php | 23 + database/factories/SprintflowFactory.php | 16 + database/factories/SystemFactory.php | 16 + database/factories/TagFactory.php | 15 + database/factories/TaskFactory.php | 22 + database/factories/TransactionFactory.php | 21 + database/factories/UserFactory.php | 55 +- database/factories/WorkflowFactory.php | 15 + database/factories/WorkstatusFactory.php | 15 + database/migrations/.gitkeep | 0 ..._08_19_000000_create_failed_jobs_table.php | 36 - ... 2020_08_18_085016_create_users_table.php} | 10 +- .../2020_08_18_085017_fingerprints.php | 38 + ...0_08_18_085018_create_businesses_table.php | 49 + ...020_08_18_085046_create_projects_table.php | 58 + ...0_08_18_085054_create_workflows_table.php} | 13 +- ...020_08_18_085102_create_statuses_table.php | 39 + .../2020_08_18_085114_create_tags_table.php | 41 + ...2020_08_28_095802_create_systems_table.php | 34 + .../2020_08_28_101545_create_sprint_table.php | 38 + ...10_31_182018_create_transactions_table.php | 37 + .../2021_09_03_085114_create_costs_table.php | 41 + .../2022_10_13_085114_create_files_table.php | 45 + ..._13_085115_create_files_relation_table.php | 35 + ...23_10_14_085115_create_media_table.php.php | 31 + database/seeders/DatabaseSeeder.php | 18 - database/seeds/BusinessSeeder.php | 144 + database/seeds/CostSeeder.php | 14 + database/seeds/DatabaseSeeder.php | 24 + database/seeds/FileSeeder.php | 13 + database/seeds/ProjectSeeder.php | 123 + database/seeds/SprintSeeder.php | 21 + database/seeds/TagSeeder.php | 22 + database/seeds/TaskSeeder.php | 62 + database/seeds/TransactionSeeder.php | 14 + database/seeds/UserSeeder.php | 20 + database/seeds/WorkflowSeeder.php | 51 + routes/api.php | 178 +- 116 files changed, 9796 insertions(+), 1298 deletions(-) create mode 100644 app/Console/Commands/CostCommand.php create mode 100644 app/Http/Controllers/AuthController.php create mode 100644 app/Http/Controllers/BusinessController.php create mode 100644 app/Http/Controllers/CreditController.php create mode 100644 app/Http/Controllers/FileController.php create mode 100644 app/Http/Controllers/InvoiceController.php create mode 100644 app/Http/Controllers/ProjectController.php create mode 100644 app/Http/Controllers/SprintController.php create mode 100644 app/Http/Controllers/StatusController.php create mode 100644 app/Http/Controllers/SystemController.php create mode 100644 app/Http/Controllers/TagController.php create mode 100644 app/Http/Controllers/TaskFileController.php create mode 100644 app/Http/Controllers/UserController.php create mode 100644 app/Http/Controllers/WorkflowController.php create mode 100644 app/Http/Resources/BusinessResource.php create mode 100644 app/Http/Resources/FileResource.php create mode 100644 app/Http/Resources/FingerprintResource.php create mode 100644 app/Http/Resources/ProjectResource.php create mode 100644 app/Http/Resources/TransactionResource.php create mode 100644 app/Http/Resources/UserResource.php create mode 100644 app/Models/Activity.php create mode 100644 app/Models/Business.php create mode 100644 app/Models/Cost.php create mode 100644 app/Models/File.php create mode 100644 app/Models/Fingerprint.php create mode 100644 app/Models/Project.php create mode 100644 app/Models/SoftDeletes.php create mode 100644 app/Models/Sprint.php create mode 100644 app/Models/Status.php create mode 100644 app/Models/System.php create mode 100644 app/Models/Tag.php create mode 100644 app/Models/Task.php create mode 100644 app/Models/Transaction.php create mode 100644 app/Models/Workflow.php create mode 100644 app/Rules/MaxBound.php create mode 100644 app/Scopes/BusinessScope.php create mode 100644 app/Utilities/Avatar/DefaultConversionFileNamer.php create mode 100644 app/Utilities/Avatar/DefaultPathGenerator.php create mode 100644 app/Utilities/BusinessInfoRequestMixin.php create mode 100644 app/Utilities/Payload.php create mode 100644 app/Utilities/RequestMixin.php create mode 100644 app/Utilities/Zarinpal/Drivers/DriverInterface.php create mode 100644 app/Utilities/Zarinpal/Drivers/RestDriver.php create mode 100644 app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php create mode 100644 app/Utilities/Zarinpal/Laravel/ZarinpalServiceProvider.php create mode 100644 app/Utilities/Zarinpal/Zarinpal.php create mode 100644 config.default/app.php rename {config => config.default}/auth.php (100%) rename {config => config.default}/broadcasting.php (100%) rename {config => config.default}/cache.php (100%) create mode 100644 config.default/cors.php rename {config => config.default}/database.php (100%) create mode 100644 config.default/filesystems.php rename {config => config.default}/hashing.php (100%) create mode 100644 config.default/logging.php rename {config => config.default}/mail.php (100%) rename {config => config.default}/queue.php (100%) create mode 100644 config.default/services.php rename {config => config.default}/session.php (100%) rename {config => config.default}/view.php (100%) create mode 100644 config/amqp.php create mode 100644 config/geoip.php create mode 100644 config/media-library.php create mode 100644 config/query-builder.php create mode 100644 database/factories/BusinessFactory.php create mode 100644 database/factories/CostFactory.php create mode 100644 database/factories/FileFactory.php create mode 100644 database/factories/FingerprintFactory.php create mode 100644 database/factories/ProjectFactory.php create mode 100644 database/factories/SprintflowFactory.php create mode 100644 database/factories/SystemFactory.php create mode 100644 database/factories/TagFactory.php create mode 100644 database/factories/TaskFactory.php create mode 100644 database/factories/TransactionFactory.php create mode 100644 database/factories/WorkflowFactory.php create mode 100644 database/factories/WorkstatusFactory.php create mode 100644 database/migrations/.gitkeep delete mode 100644 database/migrations/2019_08_19_000000_create_failed_jobs_table.php rename database/migrations/{2014_10_12_000000_create_users_table.php => 2020_08_18_085016_create_users_table.php} (62%) create mode 100644 database/migrations/2020_08_18_085017_fingerprints.php create mode 100644 database/migrations/2020_08_18_085018_create_businesses_table.php create mode 100644 database/migrations/2020_08_18_085046_create_projects_table.php rename database/migrations/{2014_10_12_100000_create_password_resets_table.php => 2020_08_18_085054_create_workflows_table.php} (53%) create mode 100644 database/migrations/2020_08_18_085102_create_statuses_table.php create mode 100644 database/migrations/2020_08_18_085114_create_tags_table.php create mode 100644 database/migrations/2020_08_28_095802_create_systems_table.php create mode 100644 database/migrations/2020_08_28_101545_create_sprint_table.php create mode 100644 database/migrations/2020_10_31_182018_create_transactions_table.php create mode 100644 database/migrations/2021_09_03_085114_create_costs_table.php create mode 100644 database/migrations/2022_10_13_085114_create_files_table.php create mode 100644 database/migrations/2023_10_13_085115_create_files_relation_table.php create mode 100644 database/migrations/2023_10_14_085115_create_media_table.php.php delete mode 100644 database/seeders/DatabaseSeeder.php create mode 100644 database/seeds/BusinessSeeder.php create mode 100644 database/seeds/CostSeeder.php create mode 100644 database/seeds/DatabaseSeeder.php create mode 100644 database/seeds/FileSeeder.php create mode 100644 database/seeds/ProjectSeeder.php create mode 100644 database/seeds/SprintSeeder.php create mode 100644 database/seeds/TagSeeder.php create mode 100644 database/seeds/TaskSeeder.php create mode 100644 database/seeds/TransactionSeeder.php create mode 100644 database/seeds/UserSeeder.php create mode 100644 database/seeds/WorkflowSeeder.php diff --git a/.env.example b/.env.example index c3ed2a9..d1f8a6a 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,11 @@ -APP_NAME=Laravel +CONTAINER_NAME= + +APP_NAME=Liwo APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost +APP_TIMEZONE="Asia/Tehran" LOG_CHANNEL=stack LOG_LEVEL=debug @@ -47,3 +50,4 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php new file mode 100644 index 0000000..1ad1095 --- /dev/null +++ b/app/Console/Commands/CostCommand.php @@ -0,0 +1,136 @@ + 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; + } + } + } +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..6d41a8d --- /dev/null +++ b/app/Http/Controllers/AuthController.php @@ -0,0 +1,268 @@ +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); + } +} diff --git a/app/Http/Controllers/BusinessController.php b/app/Http/Controllers/BusinessController.php new file mode 100644 index 0000000..179837c --- /dev/null +++ b/app/Http/Controllers/BusinessController.php @@ -0,0 +1,195 @@ +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); + } + } + +} diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php new file mode 100644 index 0000000..e430404 --- /dev/null +++ b/app/Http/Controllers/CreditController.php @@ -0,0 +1,69 @@ +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("تراکنش تایید نشد"); + } +} diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php new file mode 100644 index 0000000..b57455c --- /dev/null +++ b/app/Http/Controllers/FileController.php @@ -0,0 +1,203 @@ +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(); + } +} diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php new file mode 100644 index 0000000..1bf3ea2 --- /dev/null +++ b/app/Http/Controllers/InvoiceController.php @@ -0,0 +1,80 @@ +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(); + } +} diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php new file mode 100644 index 0000000..707625d --- /dev/null +++ b/app/Http/Controllers/ProjectController.php @@ -0,0 +1,159 @@ +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; + } +} diff --git a/app/Http/Controllers/SprintController.php b/app/Http/Controllers/SprintController.php new file mode 100644 index 0000000..7f26dcd --- /dev/null +++ b/app/Http/Controllers/SprintController.php @@ -0,0 +1,47 @@ + $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); + } + +} diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php new file mode 100644 index 0000000..7740503 --- /dev/null +++ b/app/Http/Controllers/StatusController.php @@ -0,0 +1,37 @@ +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); + } +} diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php new file mode 100644 index 0000000..aadc34b --- /dev/null +++ b/app/Http/Controllers/SystemController.php @@ -0,0 +1,40 @@ + $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); + } + + +} diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php new file mode 100644 index 0000000..7d661ba --- /dev/null +++ b/app/Http/Controllers/TagController.php @@ -0,0 +1,37 @@ +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); + } + +} diff --git a/app/Http/Controllers/TaskFileController.php b/app/Http/Controllers/TaskFileController.php new file mode 100644 index 0000000..ab35061 --- /dev/null +++ b/app/Http/Controllers/TaskFileController.php @@ -0,0 +1,89 @@ +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(); + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..fba4e8d --- /dev/null +++ b/app/Http/Controllers/UserController.php @@ -0,0 +1,69 @@ +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; + } +} diff --git a/app/Http/Controllers/WorkflowController.php b/app/Http/Controllers/WorkflowController.php new file mode 100644 index 0000000..80fdc3d --- /dev/null +++ b/app/Http/Controllers/WorkflowController.php @@ -0,0 +1,71 @@ +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); + } + +} diff --git a/app/Http/Resources/BusinessResource.php b/app/Http/Resources/BusinessResource.php new file mode 100644 index 0000000..b448dc5 --- /dev/null +++ b/app/Http/Resources/BusinessResource.php @@ -0,0 +1,34 @@ + '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; + } +} diff --git a/app/Http/Resources/FileResource.php b/app/Http/Resources/FileResource.php new file mode 100644 index 0000000..3b74fb0 --- /dev/null +++ b/app/Http/Resources/FileResource.php @@ -0,0 +1,37 @@ + '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; + } +} diff --git a/app/Http/Resources/FingerprintResource.php b/app/Http/Resources/FingerprintResource.php new file mode 100644 index 0000000..2c2eded --- /dev/null +++ b/app/Http/Resources/FingerprintResource.php @@ -0,0 +1,34 @@ + '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; + } +} diff --git a/app/Http/Resources/ProjectResource.php b/app/Http/Resources/ProjectResource.php new file mode 100644 index 0000000..8800864 --- /dev/null +++ b/app/Http/Resources/ProjectResource.php @@ -0,0 +1,38 @@ + '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; + } +} diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php new file mode 100644 index 0000000..89b07df --- /dev/null +++ b/app/Http/Resources/TransactionResource.php @@ -0,0 +1,32 @@ + '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; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php new file mode 100644 index 0000000..5bf3f1f --- /dev/null +++ b/app/Http/Resources/UserResource.php @@ -0,0 +1,34 @@ + '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; + } +} diff --git a/app/Models/Activity.php b/app/Models/Activity.php new file mode 100644 index 0000000..cf4c1d1 --- /dev/null +++ b/app/Models/Activity.php @@ -0,0 +1,10 @@ + '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; + } +} diff --git a/app/Models/Cost.php b/app/Models/Cost.php new file mode 100644 index 0000000..9d29a26 --- /dev/null +++ b/app/Models/Cost.php @@ -0,0 +1,24 @@ + 'array', + 'tax' => 'float', + ]; + + public function business() + { + return $this->belongsTo(Business::class, 'business_id'); + } +} diff --git a/app/Models/File.php b/app/Models/File.php new file mode 100644 index 0000000..f486228 --- /dev/null +++ b/app/Models/File.php @@ -0,0 +1,75 @@ + '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, + ] + ); + } +} diff --git a/app/Models/Fingerprint.php b/app/Models/Fingerprint.php new file mode 100644 index 0000000..39866c6 --- /dev/null +++ b/app/Models/Fingerprint.php @@ -0,0 +1,30 @@ +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', + ]; + } +} diff --git a/app/Models/Project.php b/app/Models/Project.php new file mode 100644 index 0000000..d1cdf11 --- /dev/null +++ b/app/Models/Project.php @@ -0,0 +1,204 @@ + '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; + } +} diff --git a/app/Models/SoftDeletes.php b/app/Models/SoftDeletes.php new file mode 100644 index 0000000..53b6836 --- /dev/null +++ b/app/Models/SoftDeletes.php @@ -0,0 +1,29 @@ +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(); + } +} diff --git a/app/Models/Sprint.php b/app/Models/Sprint.php new file mode 100644 index 0000000..c1ad584 --- /dev/null +++ b/app/Models/Sprint.php @@ -0,0 +1,73 @@ + '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'); + } + +} diff --git a/app/Models/Status.php b/app/Models/Status.php new file mode 100644 index 0000000..8c836b0 --- /dev/null +++ b/app/Models/Status.php @@ -0,0 +1,55 @@ +'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'); + } +} diff --git a/app/Models/System.php b/app/Models/System.php new file mode 100644 index 0000000..0a7da6b --- /dev/null +++ b/app/Models/System.php @@ -0,0 +1,69 @@ + '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'); + } + +} diff --git a/app/Models/Tag.php b/app/Models/Tag.php new file mode 100644 index 0000000..6e2c425 --- /dev/null +++ b/app/Models/Tag.php @@ -0,0 +1,58 @@ + $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__ + ); + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..f29906b --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,25 @@ +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')); + } +} diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php new file mode 100644 index 0000000..7cb1a4e --- /dev/null +++ b/app/Models/Transaction.php @@ -0,0 +1,164 @@ + '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(); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 804799b..1a47d13 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -1,43 +1,184 @@ '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; + } } diff --git a/app/Models/Workflow.php b/app/Models/Workflow.php new file mode 100644 index 0000000..3446f7d --- /dev/null +++ b/app/Models/Workflow.php @@ -0,0 +1,88 @@ + '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'); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index ce74491..a5cc37f 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,8 +2,10 @@ namespace App\Providers; +use App\Fingerprint; +use App\Utilities\RequestMixin; +use App\Utilities\BusinessInfoRequestMixin; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; -use Illuminate\Support\Facades\Gate; class AuthServiceProvider extends ServiceProvider { @@ -25,6 +27,21 @@ class AuthServiceProvider extends ServiceProvider { $this->registerPolicies(); - // + $this->app['request']->mixin(new RequestMixin); + $this->app['request']->mixin(new BusinessInfoRequestMixin); + + $this->app['auth']->viaRequest('api', function ($request) { + if ($request->bearerToken() === null) { + return null; + } + + $fingerprint = Fingerprint::where([ + 'token' => $request->bearerToken(), + 'agent' => $request->getAgent(), + 'os' => $request->getOS(), + ])->first(); + + return $fingerprint->user->setAttribute('token', $fingerprint->token); + }); } } diff --git a/app/Rules/MaxBound.php b/app/Rules/MaxBound.php new file mode 100644 index 0000000..b4a530e --- /dev/null +++ b/app/Rules/MaxBound.php @@ -0,0 +1,40 @@ +bound; + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The :attribute is out of bound.'; + } +} diff --git a/app/Scopes/BusinessScope.php b/app/Scopes/BusinessScope.php new file mode 100644 index 0000000..7f99783 --- /dev/null +++ b/app/Scopes/BusinessScope.php @@ -0,0 +1,18 @@ +where('business_id', '=', request('business_id')); + } +} diff --git a/app/Utilities/Avatar/DefaultConversionFileNamer.php b/app/Utilities/Avatar/DefaultConversionFileNamer.php new file mode 100644 index 0000000..2e11692 --- /dev/null +++ b/app/Utilities/Avatar/DefaultConversionFileNamer.php @@ -0,0 +1,15 @@ +model->getKey(); + } +} diff --git a/app/Utilities/Avatar/DefaultPathGenerator.php b/app/Utilities/Avatar/DefaultPathGenerator.php new file mode 100644 index 0000000..1b21029 --- /dev/null +++ b/app/Utilities/Avatar/DefaultPathGenerator.php @@ -0,0 +1,41 @@ +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/'; + } +} diff --git a/app/Utilities/BusinessInfoRequestMixin.php b/app/Utilities/BusinessInfoRequestMixin.php new file mode 100644 index 0000000..5966ae3 --- /dev/null +++ b/app/Utilities/BusinessInfoRequestMixin.php @@ -0,0 +1,43 @@ +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'); + }; + } +} diff --git a/app/Utilities/Payload.php b/app/Utilities/Payload.php new file mode 100644 index 0000000..a845b28 --- /dev/null +++ b/app/Utilities/Payload.php @@ -0,0 +1,83 @@ +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]); + } +} diff --git a/app/Utilities/RequestMixin.php b/app/Utilities/RequestMixin.php new file mode 100644 index 0000000..c53192e --- /dev/null +++ b/app/Utilities/RequestMixin.php @@ -0,0 +1,51 @@ + $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; + } +} diff --git a/app/Utilities/Zarinpal/Drivers/DriverInterface.php b/app/Utilities/Zarinpal/Drivers/DriverInterface.php new file mode 100644 index 0000000..8227518 --- /dev/null +++ b/app/Utilities/Zarinpal/Drivers/DriverInterface.php @@ -0,0 +1,46 @@ +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/'); + } +} diff --git a/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php b/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php new file mode 100644 index 0000000..921c2fb --- /dev/null +++ b/app/Utilities/Zarinpal/Laravel/Facade/Zarinpal.php @@ -0,0 +1,24 @@ +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() + { + // + } +} diff --git a/app/Utilities/Zarinpal/Zarinpal.php b/app/Utilities/Zarinpal/Zarinpal.php new file mode 100644 index 0000000..6339b33 --- /dev/null +++ b/app/Utilities/Zarinpal/Zarinpal.php @@ -0,0 +1,130 @@ +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'; + } +} diff --git a/composer.json b/composer.json index 3795a6d..5139383 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,21 @@ "license": "MIT", "require": { "php": "^7.3|^8.0", + "ext-gd": "*", + "ext-json": "*", + "anik/amqp": "^1.3", + "illuminate/notifications": "^8.0", + "jenssegers/agent": "^2.6", + "laravel/legacy-factories": "^1", + "laravel/lumen-framework": "^8.0", + "laravel/socialite": "^5.1", + "league/flysystem-aws-s3-v3": "~1.0", + "league/flysystem-cached-adapter": "~1.0", + "morilog/jalali": "3.*", + "spatie/image": "^1.0", + "spatie/laravel-medialibrary": "^8.0", + "spatie/laravel-query-builder": "^3.3", + "torann/geoip": "^3.0", "fideloper/proxy": "^4.4", "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", diff --git a/composer.lock b/composer.lock index a5ae24e..10b1f47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,63 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4a16c3c541bd99241cab1c21ce8e83ac", + "content-hash": "990d5c126e6930062665fabddea7749a", "packages": [ + { + "name": "anik/amqp", + "version": "v1.5", + "source": { + "type": "git", + "url": "https://github.com/ssi-anik/amqp.git", + "reference": "091620674aefc6bd5666be2fd9cbbdccfded93cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ssi-anik/amqp/zipball/091620674aefc6bd5666be2fd9cbbdccfded93cc", + "reference": "091620674aefc6bd5666be2fd9cbbdccfded93cc", + "shasum": "" + }, + "require": { + "illuminate/container": "^5.8|^6.0|^7.0|^8.0", + "illuminate/pipeline": "^5.8|^6.0|^7.0|^8.0", + "illuminate/support": "^5.8|^6.0|^7.0|^8.0", + "laravel/helpers": "^1.2", + "php": ">=7.0", + "php-amqplib/php-amqplib": "^2.9 !=2.12.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Anik\\Amqp\\ServiceProviders\\AmqpServiceProvider" + ], + "aliases": { + "Amqp": "Anik\\Amqp\\Facades\\Amqp" + } + } + }, + "autoload": { + "psr-4": { + "Anik\\Amqp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Syed Sirajul Islam Anik", + "email": "sirajul.islam.anik@gmail.com" + } + ], + "description": "php-amqplib wrapper that eases the consumption of RabbitMQ. A painless way of using RabbitMQ", + "support": { + "issues": "https://github.com/ssi-anik/amqp/issues", + "source": "https://github.com/ssi-anik/amqp/tree/v1.5" + }, + "time": "2020-09-25T00:37:54+00:00" + }, { "name": "asm89/stack-cors", "version": "v2.0.2", @@ -56,8 +111,167 @@ "cors", "stack" ], + "support": { + "issues": "https://github.com/asm89/stack-cors/issues", + "source": "https://github.com/asm89/stack-cors/tree/v2.0.2" + }, "time": "2020-10-29T16:03:21+00:00" }, + { + "name": "aws/aws-sdk-php", + "version": "3.173.19", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "63c6feca49bf4083f33bf250401c0c286759e101" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/63c6feca49bf4083f33bf250401c0c286759e101", + "reference": "63c6feca49bf4083f33bf250401c0c286759e101", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.173.19" + }, + "time": "2021-03-01T19:15:59+00:00" + }, + { + "name": "beberlei/assert", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/beberlei/assert.git", + "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beberlei/assert/zipball/5367e3895976b49704ae671f75bc5f0ba1b986ab", + "reference": "5367e3895976b49704ae671f75bc5f0ba1b986ab", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "*", + "phpstan/phpstan": "*", + "phpunit/phpunit": ">=6.0.0", + "yoast/phpunit-polyfills": "^0.1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Assert\\": "lib/Assert" + }, + "files": [ + "lib/Assert/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de", + "role": "Lead Developer" + }, + { + "name": "Richard Quadling", + "email": "rquadling@gmail.com", + "role": "Collaborator" + } + ], + "description": "Thin assertion library for input validation in business models.", + "keywords": [ + "assert", + "assertion", + "validation" + ], + "support": { + "issues": "https://github.com/beberlei/assert/issues", + "source": "https://github.com/beberlei/assert/tree/v3.3.0" + }, + "time": "2020-11-13T20:02:54+00:00" + }, { "name": "brick/math", "version": "0.9.2", @@ -102,6 +316,10 @@ "brick", "math" ], + "support": { + "issues": "https://github.com/brick/math/issues", + "source": "https://github.com/brick/math/tree/0.9.2" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/brick/math", @@ -141,6 +359,10 @@ "MIT" ], "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, "time": "2019-12-04T15:06:13+00:00" }, { @@ -218,6 +440,10 @@ "uppercase", "words" ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.x" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -294,6 +520,10 @@ "parser", "php" ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.2.1" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -359,6 +589,10 @@ "cron", "schedule" ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.1.0" + }, "funding": [ { "url": "https://github.com/dragonmantank", @@ -423,6 +657,10 @@ "validation", "validator" ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/2.1.25" + }, "funding": [ { "url": "https://github.com/egulias", @@ -483,6 +721,10 @@ "proxy", "trusted proxy" ], + "support": { + "issues": "https://github.com/fideloper/TrustedProxy/issues", + "source": "https://github.com/fideloper/TrustedProxy/tree/4.4.1" + }, "time": "2020-10-22T13:48:01+00:00" }, { @@ -550,6 +792,10 @@ "crossdomain", "laravel" ], + "support": { + "issues": "https://github.com/fruitcake/laravel-cors/issues", + "source": "https://github.com/fruitcake/laravel-cors/tree/v2.0.3" + }, "funding": [ { "url": "https://github.com/barryvdh", @@ -608,6 +854,10 @@ "Result-Type", "result" ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.1" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -698,6 +948,10 @@ "rest", "web service" ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.2.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -767,6 +1021,10 @@ "keywords": [ "promise" ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.4.0" + }, "time": "2020-09-30T07:37:28+00:00" }, { @@ -838,143 +1096,356 @@ "uri", "url" ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.7.0" + }, "time": "2020-09-30T07:37:11+00:00" }, { - "name": "laravel/framework", - "version": "v8.29.0", + "name": "intervention/image", + "version": "2.5.1", "source": { "type": "git", - "url": "https://github.com/laravel/framework.git", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e" + "url": "https://github.com/Intervention/image.git", + "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d2eba352b3b3a3c515b18c5726b373fe5026733e", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e", + "url": "https://api.github.com/repos/Intervention/image/zipball/abbf18d5ab8367f96b3205ca3c89fb2fa598c69e", + "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e", "shasum": "" }, "require": { - "doctrine/inflector": "^1.4|^2.0", - "dragonmantank/cron-expression": "^3.0.2", - "egulias/email-validator": "^2.1.10", - "ext-json": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "league/commonmark": "^1.3", - "league/flysystem": "^1.1", - "monolog/monolog": "^2.0", - "nesbot/carbon": "^2.31", - "opis/closure": "^3.6", - "php": "^7.3|^8.0", - "psr/container": "^1.0", - "psr/simple-cache": "^1.0", - "ramsey/uuid": "^4.0", - "swiftmailer/swiftmailer": "^6.0", - "symfony/console": "^5.1.4", - "symfony/error-handler": "^5.1.4", - "symfony/finder": "^5.1.4", - "symfony/http-foundation": "^5.1.4", - "symfony/http-kernel": "^5.1.4", - "symfony/mime": "^5.1.4", - "symfony/process": "^5.1.4", - "symfony/routing": "^5.1.4", - "symfony/var-dumper": "^5.1.4", - "tijsverkoyen/css-to-inline-styles": "^2.2.2", - "vlucas/phpdotenv": "^5.2", - "voku/portable-ascii": "^1.4.8" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "provide": { - "psr/container-implementation": "1.0" - }, - "replace": { - "illuminate/auth": "self.version", - "illuminate/broadcasting": "self.version", - "illuminate/bus": "self.version", - "illuminate/cache": "self.version", - "illuminate/collections": "self.version", - "illuminate/config": "self.version", - "illuminate/console": "self.version", - "illuminate/container": "self.version", - "illuminate/contracts": "self.version", - "illuminate/cookie": "self.version", - "illuminate/database": "self.version", - "illuminate/encryption": "self.version", - "illuminate/events": "self.version", - "illuminate/filesystem": "self.version", - "illuminate/hashing": "self.version", - "illuminate/http": "self.version", - "illuminate/log": "self.version", - "illuminate/macroable": "self.version", - "illuminate/mail": "self.version", - "illuminate/notifications": "self.version", - "illuminate/pagination": "self.version", - "illuminate/pipeline": "self.version", - "illuminate/queue": "self.version", - "illuminate/redis": "self.version", - "illuminate/routing": "self.version", - "illuminate/session": "self.version", - "illuminate/support": "self.version", - "illuminate/testing": "self.version", - "illuminate/translation": "self.version", - "illuminate/validation": "self.version", - "illuminate/view": "self.version" + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.4.0" }, "require-dev": { - "aws/aws-sdk-php": "^3.155", - "doctrine/dbal": "^2.6|^3.0", - "filp/whoops": "^2.8", - "guzzlehttp/guzzle": "^6.5.5|^7.0.1", - "league/flysystem-cached-adapter": "^1.0", - "mockery/mockery": "^1.4.2", - "orchestra/testbench-core": "^6.8", - "pda/pheanstalk": "^4.0", - "phpunit/phpunit": "^8.5.8|^9.3.3", - "predis/predis": "^1.1.1", - "symfony/cache": "^5.1.4" + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "^4.8 || ^5.7" }, "suggest": { - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).", - "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", - "ext-ftp": "Required to use the Flysystem FTP driver.", - "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", - "ext-memcached": "Required to use the memcache cache driver.", - "ext-pcntl": "Required to use all features of the queue worker.", - "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", - "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", - "filp/whoops": "Required for friendly error pages in development (^2.8).", - "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", - "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", - "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", - "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", - "mockery/mockery": "Required to use mocking (^1.4.2).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).", - "predis/predis": "Required to use the predis connector (^1.1.2).", - "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", - "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", - "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "8.x-dev" + "dev-master": "2.4-dev" + }, + "laravel": { + "providers": [ + "Intervention\\Image\\ImageServiceProvider" + ], + "aliases": { + "Image": "Intervention\\Image\\Facades\\Image" + } } }, "autoload": { - "files": [ - "src/Illuminate/Collections/helpers.php", - "src/Illuminate/Events/functions.php", + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@olivervogel.com", + "homepage": "http://olivervogel.com/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "support": { + "issues": "https://github.com/Intervention/image/issues", + "source": "https://github.com/Intervention/image/tree/master" + }, + "time": "2019-11-02T09:15:47+00:00" + }, + { + "name": "jaybizzle/crawler-detect", + "version": "v1.2.104", + "source": { + "type": "git", + "url": "https://github.com/JayBizzle/Crawler-Detect.git", + "reference": "a581e89a9212c4e9d18049666dc735718c29de9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/a581e89a9212c4e9d18049666dc735718c29de9c", + "reference": "a581e89a9212c4e9d18049666dc735718c29de9c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jaybizzle\\CrawlerDetect\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Beech", + "email": "m@rkbee.ch", + "role": "Developer" + } + ], + "description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent", + "homepage": "https://github.com/JayBizzle/Crawler-Detect/", + "keywords": [ + "crawler", + "crawler detect", + "crawler detector", + "crawlerdetect", + "php crawler detect" + ], + "support": { + "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.104" + }, + "time": "2021-01-13T15:25:20+00:00" + }, + { + "name": "jenssegers/agent", + "version": "v2.6.4", + "source": { + "type": "git", + "url": "https://github.com/jenssegers/agent.git", + "reference": "daa11c43729510b3700bc34d414664966b03bffe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jenssegers/agent/zipball/daa11c43729510b3700bc34d414664966b03bffe", + "reference": "daa11c43729510b3700bc34d414664966b03bffe", + "shasum": "" + }, + "require": { + "jaybizzle/crawler-detect": "^1.2", + "mobiledetect/mobiledetectlib": "^2.7.6", + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5.0|^6.0|^7.0" + }, + "suggest": { + "illuminate/support": "Required for laravel service providers" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + }, + "laravel": { + "providers": [ + "Jenssegers\\Agent\\AgentServiceProvider" + ], + "aliases": { + "Agent": "Jenssegers\\Agent\\Facades\\Agent" + } + } + }, + "autoload": { + "psr-4": { + "Jenssegers\\Agent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jens Segers", + "homepage": "https://jenssegers.com" + } + ], + "description": "Desktop/mobile user agent parser with support for Laravel, based on Mobiledetect", + "homepage": "https://github.com/jenssegers/agent", + "keywords": [ + "Agent", + "browser", + "desktop", + "laravel", + "mobile", + "platform", + "user agent", + "useragent" + ], + "support": { + "issues": "https://github.com/jenssegers/agent/issues", + "source": "https://github.com/jenssegers/agent/tree/v2.6.4" + }, + "funding": [ + { + "url": "https://github.com/jenssegers", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/jenssegers/agent", + "type": "tidelift" + } + ], + "time": "2020-06-13T08:05:20+00:00" + }, + { + "name": "laravel/framework", + "version": "v8.29.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/d2eba352b3b3a3c515b18c5726b373fe5026733e", + "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^1.4|^2.0", + "dragonmantank/cron-expression": "^3.0.2", + "egulias/email-validator": "^2.1.10", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "league/commonmark": "^1.3", + "league/flysystem": "^1.1", + "monolog/monolog": "^2.0", + "nesbot/carbon": "^2.31", + "opis/closure": "^3.6", + "php": "^7.3|^8.0", + "psr/container": "^1.0", + "psr/simple-cache": "^1.0", + "ramsey/uuid": "^4.0", + "swiftmailer/swiftmailer": "^6.0", + "symfony/console": "^5.1.4", + "symfony/error-handler": "^5.1.4", + "symfony/finder": "^5.1.4", + "symfony/http-foundation": "^5.1.4", + "symfony/http-kernel": "^5.1.4", + "symfony/mime": "^5.1.4", + "symfony/process": "^5.1.4", + "symfony/routing": "^5.1.4", + "symfony/var-dumper": "^5.1.4", + "tijsverkoyen/css-to-inline-styles": "^2.2.2", + "vlucas/phpdotenv": "^5.2", + "voku/portable-ascii": "^1.4.8" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.155", + "doctrine/dbal": "^2.6|^3.0", + "filp/whoops": "^2.8", + "guzzlehttp/guzzle": "^6.5.5|^7.0.1", + "league/flysystem-cached-adapter": "^1.0", + "mockery/mockery": "^1.4.2", + "orchestra/testbench-core": "^6.8", + "pda/pheanstalk": "^4.0", + "phpunit/phpunit": "^8.5.8|^9.3.3", + "predis/predis": "^1.1.1", + "symfony/cache": "^5.1.4" + }, + "suggest": { + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", + "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", + "filp/whoops": "Required for friendly error pages in development (^2.8).", + "guzzlehttp/guzzle": "Required to use the HTTP Client, Mailgun mail driver and the ping methods on schedules (^6.5.5|^7.0.1).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", + "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", + "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", + "mockery/mockery": "Required to use mocking (^1.4.2).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^8.5.8|^9.3.3).", + "predis/predis": "Required to use the predis connector (^1.1.2).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0|^5.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^5.1.4).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^5.1.4).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0).", + "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", "src/Illuminate/Foundation/helpers.php", "src/Illuminate/Support/helpers.php" ], @@ -992,228 +1463,1839 @@ ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-02-23T14:27:41+00:00" + }, + { + "name": "laravel/helpers", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/helpers.git", + "reference": "febb10d8daaf86123825de2cb87f789a3371f0ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/helpers/zipball/febb10d8daaf86123825de2cb87f789a3371f0ac", + "reference": "febb10d8daaf86123825de2cb87f789a3371f0ac", + "shasum": "" + }, + "require": { + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0", + "php": "^7.1.3|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries.vints@gmail.com" + } + ], + "description": "Provides backwards compatibility for helpers in the latest Laravel release.", + "keywords": [ + "helpers", + "laravel" + ], + "support": { + "source": "https://github.com/laravel/helpers/tree/v1.4.1" + }, + "time": "2021-02-16T15:27:11+00:00" + }, + { + "name": "laravel/legacy-factories", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/legacy-factories.git", + "reference": "5e3fe2fd5fda64e20ea5c74c831a7346294e902a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/legacy-factories/zipball/5e3fe2fd5fda64e20ea5c74c831a7346294e902a", + "reference": "5e3fe2fd5fda64e20ea5c74c831a7346294e902a", + "shasum": "" + }, + "require": { + "illuminate/macroable": "^8.0", + "php": "^7.3|^8.0", + "symfony/finder": "^3.4|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + }, + "laravel": { + "providers": [ + "Illuminate\\Database\\Eloquent\\LegacyFactoryServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Database\\Eloquent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The legacy version of the Laravel Eloquent factories.", + "homepage": "http://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2020-10-27T14:25:32+00:00" + }, + { + "name": "laravel/lumen-framework", + "version": "v8.2.3", + "source": { + "type": "git", + "url": "https://github.com/laravel/lumen-framework.git", + "reference": "6ed02d4d1a6e203b9e896bd105b2e838866f2951" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/lumen-framework/zipball/6ed02d4d1a6e203b9e896bd105b2e838866f2951", + "reference": "6ed02d4d1a6e203b9e896bd105b2e838866f2951", + "shasum": "" + }, + "require": { + "dragonmantank/cron-expression": "^3.0.2", + "illuminate/auth": "^8.0", + "illuminate/broadcasting": "^8.0", + "illuminate/bus": "^8.0", + "illuminate/cache": "^8.0", + "illuminate/collections": "^8.0", + "illuminate/config": "^8.0", + "illuminate/console": "^8.0", + "illuminate/container": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/database": "^8.0", + "illuminate/encryption": "^8.0", + "illuminate/events": "^8.0", + "illuminate/filesystem": "^8.0", + "illuminate/hashing": "^8.0", + "illuminate/http": "^8.0", + "illuminate/log": "^8.0", + "illuminate/macroable": "^8.0", + "illuminate/pagination": "^8.0", + "illuminate/pipeline": "^8.0", + "illuminate/queue": "^8.0", + "illuminate/support": "^8.0", + "illuminate/testing": "^8.0", + "illuminate/translation": "^8.0", + "illuminate/validation": "^8.0", + "illuminate/view": "^8.0", + "nikic/fast-route": "^1.3", + "php": "^7.3|^8.0", + "symfony/console": "^5.1", + "symfony/error-handler": "^5.1", + "symfony/http-foundation": "^5.1", + "symfony/http-kernel": "^5.1", + "symfony/mime": "^5.1", + "symfony/var-dumper": "^5.1", + "vlucas/phpdotenv": "^5.2" + }, + "require-dev": { + "mockery/mockery": "^1.4.2", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Lumen\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylorotwell@gmail.com" + } + ], + "description": "The Laravel Lumen Framework.", + "homepage": "https://lumen.laravel.com", + "keywords": [ + "framework", + "laravel", + "lumen" + ], + "support": { + "issues": "https://github.com/laravel/lumen-framework/issues", + "source": "https://github.com/laravel/lumen-framework" + }, + "time": "2021-02-09T16:42:36+00:00" + }, + { + "name": "laravel/socialite", + "version": "v5.2.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/socialite.git", + "reference": "8aa705d771f127317f60887515cfb2f777653ce5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/socialite/zipball/8aa705d771f127317f60887515cfb2f777653ce5", + "reference": "8aa705d771f127317f60887515cfb2f777653ce5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "illuminate/http": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "league/oauth1-client": "^1.0", + "php": "^7.2|^8.0" + }, + "require-dev": { + "illuminate/contracts": "^6.0|^7.0", + "mockery/mockery": "^1.0", + "orchestra/testbench": "^4.0|^5.0|^6.0", + "phpunit/phpunit": "^8.0|^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ], + "aliases": { + "Socialite": "Laravel\\Socialite\\Facades\\Socialite" + } + } + }, + "autoload": { + "psr-4": { + "Laravel\\Socialite\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.", + "homepage": "https://laravel.com", + "keywords": [ + "laravel", + "oauth" + ], + "support": { + "issues": "https://github.com/laravel/socialite/issues", + "source": "https://github.com/laravel/socialite" + }, + "time": "2021-02-22T14:22:51+00:00" + }, + { + "name": "laravel/tinker", + "version": "v2.6.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/daae1c43f1300fe88c05d83db6f3d8f76677ad88", + "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88", + "shasum": "" + }, + "require": { + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "php": "^7.2.5|^8.0", + "psy/psysh": "^0.10.4", + "symfony/var-dumper": "^4.3.4|^5.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpunit/phpunit": "^8.5.8|^9.3.3" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v2.6.0" + }, + "time": "2021-01-26T20:35:18+00:00" + }, + { + "name": "league/commonmark", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54", + "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "scrutinizer/ocular": "1.7.*" + }, + "require-dev": { + "cebe/markdown": "~1.0", + "commonmark/commonmark.js": "0.29.2", + "erusev/parsedown": "~1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "~1.4", + "mikehaertl/php-shellcommand": "^1.4", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2", + "scrutinizer/ocular": "^1.5", + "symfony/finder": "^4.2" + }, + "bin": [ + "bin/commonmark" + ], + "type": "library", + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark", + "type": "custom" + }, + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://www.patreon.com/colinodell", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2020-10-31T13:49:32+00:00" + }, + { + "name": "league/flysystem", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a", + "reference": "9be3b16c877d477357c015cec057548cf9b2a14a", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/mime-type-detection": "^1.3", + "php": "^7.2.5 || ^8.0" + }, + "conflict": { + "league/flysystem-sftp": "<1.0.6" + }, + "require-dev": { + "phpspec/prophecy": "^1.11.1", + "phpunit/phpunit": "^8.5.8" + }, + "suggest": { + "ext-fileinfo": "Required for MimeType", + "ext-ftp": "Allows you to use FTP server storage", + "ext-openssl": "Allows you to use FTPS server storage", + "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", + "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", + "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", + "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", + "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", + "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", + "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", + "league/flysystem-webdav": "Allows you to use WebDAV storage", + "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", + "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", + "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Filesystem abstraction: Many filesystems, one API.", + "keywords": [ + "Cloud Files", + "WebDAV", + "abstraction", + "aws", + "cloud", + "copy.com", + "dropbox", + "file systems", + "files", + "filesystem", + "filesystems", + "ftp", + "rackspace", + "remote", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/1.x" + }, + "funding": [ + { + "url": "https://offset.earth/frankdejonge", + "type": "other" + } + ], + "time": "2020-08-23T07:39:11+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "1.0.29", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "4e25cc0582a36a786c31115e419c6e40498f6972" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4e25cc0582a36a786c31115e419c6e40498f6972", + "reference": "4e25cc0582a36a786c31115e419c6e40498f6972", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.20.0", + "league/flysystem": "^1.0.40", + "php": ">=5.5.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "^2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3v3\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Flysystem adapter for the AWS S3 SDK v3.x", + "support": { + "issues": "https://github.com/thephpleague/flysystem-aws-s3-v3/issues", + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/1.0.29" + }, + "time": "2020-10-08T18:58:37+00:00" + }, + { + "name": "league/flysystem-cached-adapter", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-cached-adapter.git", + "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff", + "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff", + "shasum": "" + }, + "require": { + "league/flysystem": "~1.0", + "psr/cache": "^1.0.0" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "phpspec/phpspec": "^3.4", + "phpunit/phpunit": "^5.7", + "predis/predis": "~1.0", + "tedivm/stash": "~0.12" + }, + "suggest": { + "ext-phpredis": "Pure C implemented extension for PHP" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Cached\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "frankdejonge", + "email": "info@frenky.net" + } + ], + "description": "An adapter decorator to enable meta-data caching.", + "support": { + "issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues", + "source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master" + }, + "time": "2020-07-25T15:56:04+00:00" + }, + { + "name": "league/glide", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/glide.git", + "reference": "ae5e26700573cb678919d28e425a8b87bc71c546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/glide/zipball/ae5e26700573cb678919d28e425a8b87bc71c546", + "reference": "ae5e26700573cb678919d28e425a8b87bc71c546", + "shasum": "" + }, + "require": { + "intervention/image": "^2.4", + "league/flysystem": "^1.0", + "php": "^7.2|^8.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "phpunit/php-token-stream": "^3.1|^4.0", + "phpunit/phpunit": "^8.5|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Glide\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "homepage": "http://reinink.ca" + } + ], + "description": "Wonderfully easy on-demand image manipulation library with an HTTP based API.", + "homepage": "http://glide.thephpleague.com", + "keywords": [ + "ImageMagick", + "editing", + "gd", + "image", + "imagick", + "league", + "manipulation", + "processing" + ], + "support": { + "issues": "https://github.com/thephpleague/glide/issues", + "source": "https://github.com/thephpleague/glide/tree/1.7.0" + }, + "time": "2020-11-05T17:34:03+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.18", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.7.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2021-01-18T20:58:21+00:00" + }, + { + "name": "league/oauth1-client", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth1-client.git", + "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/1e7e6be2dc543bf466236fb171e5b20e1b06aee6", + "reference": "1e7e6be2dc543bf466236fb171e5b20e1b06aee6", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-openssl": "*", + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": ">=7.1||>=8.0" + }, + "require-dev": { + "ext-simplexml": "*", + "friendsofphp/php-cs-fixer": "^2.17", + "mockery/mockery": "^1.3.3", + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5||9.5" + }, + "suggest": { + "ext-simplexml": "For decoding XML-based responses." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev", + "dev-develop": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth1\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Corlett", + "email": "bencorlett@me.com", + "homepage": "http://www.webcomm.com.au", + "role": "Developer" + } + ], + "description": "OAuth 1.0 Client Library", + "keywords": [ + "Authentication", + "SSO", + "authorization", + "bitbucket", + "identity", + "idp", + "oauth", + "oauth1", + "single sign on", + "trello", + "tumblr", + "twitter" + ], + "support": { + "issues": "https://github.com/thephpleague/oauth1-client/issues", + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.9.0" + }, + "time": "2021-01-20T01:40:53+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/c4c5803cc1f93df3d2448478ef79394a5981cc58", + "reference": "c4c5803cc1f93df3d2448478ef79394a5981cc58", + "shasum": "" + }, + "require": { + "myclabs/php-enum": "^1.5", + "php": ">= 7.1", + "psr/http-message": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "ext-zip": "*", + "guzzlehttp/guzzle": ">= 6.3", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": ">= 7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "ZipStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paul Duncan", + "email": "pabs@pablotron.org" + }, + { + "name": "Jonatan Männchen", + "email": "jonatan@maennchen.ch" + }, + { + "name": "Jesse Donat", + "email": "donatj@gmail.com" + }, + { + "name": "András Kolesár", + "email": "kolesar@kolesar.hu" + } + ], + "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", + "keywords": [ + "stream", + "zip" + ], + "support": { + "issues": "https://github.com/maennchen/ZipStream-PHP/issues", + "source": "https://github.com/maennchen/ZipStream-PHP/tree/master" + }, + "funding": [ + { + "url": "https://opencollective.com/zipstream", + "type": "open_collective" + } + ], + "time": "2020-05-30T13:11:16+00:00" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.37", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "9841e3c46f5bd0739b53aed8ac677fa712943df7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/9841e3c46f5bd0739b53aed8ac677fa712943df7", + "reference": "9841e3c46f5bd0739b53aed8ac677fa712943df7", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.35||~5.7" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://mobiledetect.net", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "support": { + "issues": "https://github.com/serbanghita/Mobile-Detect/issues", + "source": "https://github.com/serbanghita/Mobile-Detect/tree/2.8.37" + }, + "funding": [ + { + "url": "https://github.com/serbanghita", + "type": "github" + } + ], + "time": "2021-02-19T21:22:57+00:00" + }, + { + "name": "monolog/monolog", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084", + "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7", + "graylog2/gelf-php": "^1.4.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpspec/prophecy": "^1.6.1", + "phpstan/phpstan": "^0.12.59", + "phpunit/phpunit": "^8.5", + "predis/predis": "^1.1", + "rollbar/rollbar": "^1.3", + "ruflin/elastica": ">=0.90 <7.0.1", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2020-12-14T13:15:25+00:00" + }, + { + "name": "morilog/jalali", + "version": "v3.2.0", + "source": { + "type": "git", + "url": "https://github.com/morilog/jalali.git", + "reference": "7ea78b84ce3b5546b01217febb2fba4915dac5e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/morilog/jalali/zipball/7ea78b84ce3b5546b01217febb2fba4915dac5e5", + "reference": "7ea78b84ce3b5546b01217febb2fba4915dac5e5", + "shasum": "" + }, + "require": { + "beberlei/assert": "3.*", + "nesbot/carbon": "^1.21 || ^2.0", + "php": "^7.0 | ^7.1 | ^7.2 | ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Morilog\\Jalali\\": "src" + }, + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Milad Rey", + "email": "miladr@gmail.com" + }, + { + "name": "Morteza Parvini", + "email": "m.parvini@outlook.com" + } + ], + "description": "This Package helps developers to easily work with Jalali (Shamsi or Iranian) dates in PHP applications, based on Jalali (Shamsi) DateTime class.", + "keywords": [ + "Jalali", + "date", + "datetime", + "laravel", + "morilog" + ], + "support": { + "issues": "https://github.com/morilog/jalali/issues", + "source": "https://github.com/morilog/jalali/tree/v3.2.0" + }, + "time": "2020-12-01T21:26:31+00:00" + }, + { + "name": "mtdowling/jmespath.php", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb", + "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^1.4", + "phpunit/phpunit": "^4.8.36 || ^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0" + }, + "time": "2020-07-31T21:01:56+00:00" + }, + { + "name": "myclabs/php-enum", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/php-enum.git", + "reference": "46cf3d8498b095bd33727b13fd5707263af99421" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/46cf3d8498b095bd33727b13fd5707263af99421", + "reference": "46cf3d8498b095bd33727b13fd5707263af99421", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^4.5.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "MyCLabs\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP Enum contributors", + "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" + } + ], + "description": "PHP Enum implementation", + "homepage": "http://github.com/myclabs/php-enum", + "keywords": [ + "enum" + ], + "support": { + "issues": "https://github.com/myclabs/php-enum/issues", + "source": "https://github.com/myclabs/php-enum/tree/1.8.0" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", + "type": "tidelift" + } + ], + "time": "2021-02-15T16:11:48+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.45.1", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "528783b188bdb853eb21239b1722831e0f000a8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", + "reference": "528783b188bdb853eb21239b1722831e0f000a8d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^3.4 || ^4.0 || ^5.0" + }, + "require-dev": { + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", + "kylekatarnls/multi-tester": "^2.0", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.54", + "phpunit/phpunit": "^7.5.20 || ^8.5.14", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev", + "dev-3.x": "3.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "kylekatarnls", + "homepage": "http://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "http://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2021-02-11T18:30:17+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + }, + "time": "2020-12-20T10:01:03+00:00" + }, + { + "name": "opis/closure", + "version": "3.6.1", + "source": { + "type": "git", + "url": "https://github.com/opis/closure.git", + "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5", + "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0" + }, + "require-dev": { + "jeremeamia/superclosure": "^2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Opis\\Closure\\": "src/" + }, + "files": [ + "functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", + "homepage": "https://opis.io/closure", + "keywords": [ + "anonymous functions", + "closure", + "function", + "serializable", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/opis/closure/issues", + "source": "https://github.com/opis/closure/tree/3.6.1" + }, + "time": "2020-11-07T02:01:34+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c", + "reference": "f34c2b11eb9d2c9318e13540a1dbc2a3afbd939c", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2020-12-06T15:14:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" } ], - "description": "The Laravel Framework.", - "homepage": "https://laravel.com", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ - "framework", - "laravel" + "csprng", + "polyfill", + "pseudorandom", + "random" ], - "time": "2021-02-23T14:27:41+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" }, { - "name": "laravel/tinker", - "version": "v2.6.0", + "name": "php-amqplib/php-amqplib", + "version": "v2.12.3", "source": { "type": "git", - "url": "https://github.com/laravel/tinker.git", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88" + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "f746eb44df6d8f838173729867dd1d20b0265faa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/daae1c43f1300fe88c05d83db6f3d8f76677ad88", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/f746eb44df6d8f838173729867dd1d20b0265faa", + "reference": "f746eb44df6d8f838173729867dd1d20b0265faa", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0", - "illuminate/contracts": "^6.0|^7.0|^8.0", - "illuminate/support": "^6.0|^7.0|^8.0", - "php": "^7.2.5|^8.0", - "psy/psysh": "^0.10.4", - "symfony/var-dumper": "^4.3.4|^5.0" + "ext-mbstring": "*", + "ext-sockets": "*", + "php": ">=5.6.3,<8.0", + "phpseclib/phpseclib": "^2.0|^3.0" }, - "require-dev": { - "mockery/mockery": "~1.3.3|^1.4.2", - "phpunit/phpunit": "^8.5.8|^9.3.3" + "conflict": { + "php": "7.4.0 - 7.4.1" }, - "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0)." + "replace": { + "videlalvaro/php-amqplib": "self.version" + }, + "require-dev": { + "ext-curl": "*", + "nategood/httpful": "^0.2.20", + "phpunit/phpunit": "^5.7|^6.5|^7.0", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" - }, - "laravel": { - "providers": [ - "Laravel\\Tinker\\TinkerServiceProvider" - ] + "dev-master": "2.12-dev" } }, "autoload": { "psr-4": { - "Laravel\\Tinker\\": "src/" + "PhpAmqpLib\\": "PhpAmqpLib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "LGPL-2.1-or-later" ], "authors": [ { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + }, + { + "name": "Luke Bakken", + "email": "luke@bakken.io", + "role": "Maintainer" + }, + { + "name": "Ramūnas Dronga", + "email": "github@ramuno.lt", + "role": "Maintainer" } ], - "description": "Powerful REPL for the Laravel framework.", + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", "keywords": [ - "REPL", - "Tinker", - "laravel", - "psysh" + "message", + "queue", + "rabbitmq" ], - "time": "2021-01-26T20:35:18+00:00" + "support": { + "issues": "https://github.com/php-amqplib/php-amqplib/issues", + "source": "https://github.com/php-amqplib/php-amqplib/tree/v2.12.3" + }, + "time": "2021-03-01T12:21:31+00:00" }, { - "name": "league/commonmark", - "version": "1.5.7", + "name": "phpoption/phpoption", + "version": "1.7.5", "source": { "type": "git", - "url": "https://github.com/thephpleague/commonmark.git", - "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54" + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54", - "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "scrutinizer/ocular": "1.7.*" + "php": "^5.5.9 || ^7.0 || ^8.0" }, "require-dev": { - "cebe/markdown": "~1.0", - "commonmark/commonmark.js": "0.29.2", - "erusev/parsedown": "~1.0", - "ext-json": "*", - "github/gfm": "0.29.0", - "michelf/php-markdown": "~1.4", - "mikehaertl/php-shellcommand": "^1.4", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2", - "scrutinizer/ocular": "^1.5", - "symfony/finder": "^4.2" + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" }, - "bin": [ - "bin/commonmark" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, "autoload": { "psr-4": { - "League\\CommonMark\\": "src" + "PhpOption\\": "src/PhpOption/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "Apache-2.0" ], "authors": [ { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Lead Developer" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" } ], - "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and Github-Flavored Markdown (GFM)", - "homepage": "https://commonmark.thephpleague.com", + "description": "Option Type for PHP", "keywords": [ - "commonmark", - "flavored", - "gfm", - "github", - "github-flavored", - "markdown", - "md", - "parser" + "language", + "option", + "php", + "type" ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.7.5" + }, "funding": [ { - "url": "https://enjoy.gitstore.app/repositories/thephpleague/commonmark", - "type": "custom" - }, - { - "url": "https://www.colinodell.com/sponsor", - "type": "custom" - }, - { - "url": "https://www.paypal.me/colinpodell/10.00", - "type": "custom" - }, - { - "url": "https://github.com/colinodell", + "url": "https://github.com/GrahamCampbell", "type": "github" }, { - "url": "https://www.patreon.com/colinodell", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", "type": "tidelift" } ], - "time": "2020-10-31T13:49:32+00:00" + "time": "2020-07-20T17:29:33+00:00" }, { - "name": "league/flysystem", - "version": "1.1.3", + "name": "phpseclib/phpseclib", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/thephpleague/flysystem.git", - "reference": "9be3b16c877d477357c015cec057548cf9b2a14a" + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a", - "reference": "9be3b16c877d477357c015cec057548cf9b2a14a", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7c751ea006577e4c2e83326d90c8b1e8c11b8ede", + "reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "league/mime-type-detection": "^1.3", - "php": "^7.2.5 || ^8.0" - }, - "conflict": { - "league/flysystem-sftp": "<1.0.6" + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" }, "require-dev": { - "phpspec/prophecy": "^1.11.1", - "phpunit/phpunit": "^8.5.8" + "phing/phing": "~2.7", + "phpunit/phpunit": "^5.7|^6.0|^9.4", + "squizlabs/php_codesniffer": "~2.0" }, "suggest": { - "ext-fileinfo": "Required for MimeType", - "ext-ftp": "Allows you to use FTP server storage", - "ext-openssl": "Allows you to use FTPS server storage", - "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", - "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3", - "league/flysystem-azure": "Allows you to use Windows Azure Blob storage", - "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching", - "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem", - "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files", - "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib", - "league/flysystem-webdav": "Allows you to use WebDAV storage", - "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter", - "spatie/flysystem-dropbox": "Allows you to use Dropbox storage", - "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications" + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { - "League\\Flysystem\\": "src/" + "phpseclib3\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1222,65 +3304,98 @@ ], "authors": [ { - "name": "Frank de Jonge", - "email": "info@frenky.net" + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" } ], - "description": "Filesystem abstraction: Many filesystems, one API.", + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", "keywords": [ - "Cloud Files", - "WebDAV", - "abstraction", - "aws", - "cloud", - "copy.com", - "dropbox", - "file systems", - "files", - "filesystem", - "filesystems", - "ftp", - "rackspace", - "remote", - "s3", + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", "sftp", - "storage" + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.5" + }, "funding": [ { - "url": "https://offset.earth/frankdejonge", - "type": "other" + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" } ], - "time": "2020-08-23T07:39:11+00:00" + "time": "2021-02-12T16:18:16+00:00" }, { - "name": "league/mime-type-detection", - "version": "1.7.0", + "name": "psr/cache", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3" + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" }, "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", - "reference": "3b9dff8aaf7323590c1d2e443db701eb1f9aa0d3", + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.18", - "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "php": ">=5.3.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "League\\MimeTypeDetection\\": "src" + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1289,83 +3404,47 @@ ], "authors": [ { - "name": "Frank de Jonge", - "email": "info@frankdejonge.nl" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Mime-type detection for Flysystem", - "funding": [ - { - "url": "https://github.com/frankdejonge", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "type": "tidelift" - } + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" ], - "time": "2021-01-18T20:58:21+00:00" + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" }, { - "name": "monolog/monolog", - "version": "2.2.0", + "name": "psr/container", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1cb1cde8e8dd0f70cc0fe51354a59acad9302084", - "reference": "1cb1cde8e8dd0f70cc0fe51354a59acad9302084", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { - "php": ">=7.2", - "psr/log": "^1.0.1" - }, - "provide": { - "psr/log-implementation": "1.0.0" - }, - "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", - "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7", - "graylog2/gelf-php": "^1.4.2", - "mongodb/mongodb": "^1.8", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpspec/prophecy": "^1.6.1", - "phpstan/phpstan": "^0.12.59", - "phpunit/phpunit": "^8.5", - "predis/predis": "^1.1", - "rollbar/rollbar": "^1.3", - "ruflin/elastica": ">=0.90 <7.0.1", - "swiftmailer/swiftmailer": "^5.3|^6.0" - }, - "suggest": { - "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", - "doctrine/couchdb": "Allow sending log messages to a CouchDB server", - "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", - "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", - "ext-mbstring": "Allow to work properly with unicode symbols", - "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", - "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", - "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", - "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", - "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Monolog\\": "src/Monolog" + "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1374,83 +3453,51 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Sends your logs to files, sockets, inboxes, databases and various web services", - "homepage": "https://github.com/Seldaek/monolog", + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", "keywords": [ - "log", - "logging", - "psr-3" - ], - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" ], - "time": "2020-12-14T13:15:25+00:00" + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, + "time": "2017-02-14T16:28:37+00:00" }, { - "name": "nesbot/carbon", - "version": "2.45.1", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, "require": { - "ext-json": "*", - "php": "^7.1.8 || ^8.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^3.4 || ^4.0 || ^5.0" - }, - "require-dev": { - "doctrine/orm": "^2.7", - "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", - "kylekatarnls/multi-tester": "^2.0", - "phpmd/phpmd": "^2.9", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.54", - "phpunit/phpunit": "^7.5.20 || ^8.5.14", - "squizlabs/php_codesniffer": "^3.4" + "php": ">=7.2.0" }, - "bin": [ - "bin/carbon" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev", - "dev-3.x": "3.x-dev" - }, - "laravel": { - "providers": [ - "Carbon\\Laravel\\ServiceProvider" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Carbon\\": "src/Carbon/" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1459,120 +3506,101 @@ ], "authors": [ { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" - }, - { - "name": "kylekatarnls", - "homepage": "http://github.com/kylekatarnls" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "http://carbon.nesbot.com", + "description": "Standard interfaces for event handling.", "keywords": [ - "date", - "datetime", - "time" - ], - "funding": [ - { - "url": "https://opencollective.com/Carbon", - "type": "open_collective" - }, - { - "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", - "type": "tidelift" - } + "events", + "psr", + "psr-14" ], - "time": "2021-02-11T18:30:17+00:00" + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "nikic/php-parser", - "version": "v4.10.4", + "name": "psr/http-client", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", - "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" }, - "bin": [ - "bin/php-parse" - ], "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Psr\\Http\\Client\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "A PHP parser written in PHP", + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", "keywords": [ - "parser", - "php" + "http", + "http-client", + "psr", + "psr-18" ], - "time": "2020-12-20T10:01:03+00:00" + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" }, { - "name": "opis/closure", - "version": "3.6.1", + "name": "psr/http-message", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5" + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5", - "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0" - }, - "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.6.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "Opis\\Closure\\": "src/" - }, - "files": [ - "functions.php" - ] + "Psr\\Http\\Message\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1580,103 +3608,87 @@ ], "authors": [ { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" ], - "time": "2020-11-07T02:01:34+00:00" + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.7.5", + "name": "psr/log", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", - "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "PhpOption\\": "src/PhpOption/" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Apache-2.0" + "MIT" ], "authors": [ { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Graham Campbell", - "email": "graham@alt-three.com" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Option Type for PHP", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "language", - "option", - "php", - "type" - ], - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } + "log", + "psr", + "psr-3" ], - "time": "2020-07-20T17:29:33+00:00" + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, + "time": "2020-03-23T09:12:05+00:00" }, { - "name": "psr/container", - "version": "1.0.0", + "name": "psr/simple-cache", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", "shasum": "" }, "require": { @@ -1690,7 +3702,7 @@ }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "Psr\\SimpleCache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1703,43 +3715,68 @@ "homepage": "http://www.php-fig.org/" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Common interfaces for simple caching", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" ], - "time": "2017-02-14T16:28:37+00:00" + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "time": "2017-10-23T01:57:42+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "psy/psysh", + "version": "v0.10.6", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/bobthecow/psysh.git", + "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6f990c19f91729de8b31e639d6e204ea59f19cf3", + "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3", "shasum": "" }, "require": { - "php": ">=7.2.0" + "dnoegel/php-xdg-base-dir": "0.1.*", + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "~4.0|~3.0|~2.0|~1.3", + "php": "^8.0 || ^7.0 || ^5.5.9", + "symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10", + "symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "hoa/console": "3.17.*" + }, + "suggest": { + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-pdo-sqlite": "The doc command requires SQLite to work.", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", + "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", + "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." }, + "bin": [ + "bin/psysh" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "0.10.x-dev" } }, "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Psy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1748,46 +3785,51 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Justin Hileman", + "email": "justin@justinhileman.info", + "homepage": "http://justinhileman.com" } ], - "description": "Standard interfaces for event handling.", + "description": "An interactive shell for modern PHP.", + "homepage": "http://psysh.org", "keywords": [ - "events", - "psr", - "psr-14" + "REPL", + "console", + "interactive", + "shell" ], - "time": "2019-01-08T18:20:26+00:00" + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.10.6" + }, + "time": "2021-01-18T15:53:43+00:00" }, { - "name": "psr/http-client", - "version": "1.0.1", + "name": "ralouphie/getallheaders", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "php": ">=5.6" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, + "type": "library", "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1795,46 +3837,56 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "time": "2020-06-29T06:28:15+00:00" + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" }, { - "name": "psr/http-message", - "version": "1.0.1", + "name": "ramsey/collection", + "version": "1.1.3", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "url": "https://github.com/ramsey/collection.git", + "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/ramsey/collection/zipball/28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1", + "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "ergebnis/composer-normalize": "^2.6", + "fakerphp/faker": "^1.5", + "hamcrest/hamcrest-php": "^2", + "jangregor/phpstan-prophecy": "^0.8", + "mockery/mockery": "^1.3", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^0.12.32", + "phpstan/phpstan-mockery": "^0.12.5", + "phpstan/phpstan-phpunit": "^0.12.11", + "phpunit/phpunit": "^8.5 || ^9", + "psy/psysh": "^0.10.4", + "slevomat/coding-standard": "^6.3", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "Ramsey\\Collection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1843,95 +3895,159 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "A PHP 7.2+ library for representing and manipulating collections.", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "array", + "collection", + "hash", + "map", + "queue", + "set" ], - "time": "2016-08-06T14:39:51+00:00" + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/1.1.3" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", + "type": "tidelift" + } + ], + "time": "2021-01-21T17:40:04+00:00" }, { - "name": "psr/log", - "version": "1.1.3", + "name": "ramsey/uuid", + "version": "4.1.1", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + "url": "https://github.com/ramsey/uuid.git", + "reference": "cd4032040a750077205918c86049aa0f43d22947" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", - "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/cd4032040a750077205918c86049aa0f43d22947", + "reference": "cd4032040a750077205918c86049aa0f43d22947", "shasum": "" }, "require": { - "php": ">=5.3.0" + "brick/math": "^0.8 || ^0.9", + "ext-json": "*", + "php": "^7.2 || ^8", + "ramsey/collection": "^1.0", + "symfony/polyfill-ctype": "^1.8" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "codeception/aspect-mock": "^3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7.0", + "doctrine/annotations": "^1.8", + "goaop/framework": "^2", + "mockery/mockery": "^1.3", + "moontoast/math": "^1.1", + "paragonie/random-lib": "^2", + "php-mock/php-mock-mockery": "^1.3", + "php-mock/php-mock-phpunit": "^2.5", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpbench/phpbench": "^0.17.1", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-mockery": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^8.5", + "psy/psysh": "^0.10.0", + "slevomat/coding-standard": "^6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "3.9.4" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-ctype": "Enables faster processing of character classification using ctype functions.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "4.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } + "Ramsey\\Uuid\\": "src/" + }, + "files": [ + "src/functions.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "homepage": "https://github.com/ramsey/uuid", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "rss": "https://github.com/ramsey/uuid/releases.atom", + "source": "https://github.com/ramsey/uuid" + }, + "funding": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "url": "https://github.com/ramsey", + "type": "github" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2020-03-23T09:12:05+00:00" + "time": "2020-08-18T17:17:46+00:00" }, { - "name": "psr/simple-cache", - "version": "1.0.1", + "name": "spatie/image", + "version": "1.10.2", "source": { "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "url": "https://github.com/spatie/image.git", + "reference": "12662673fbe649bffcd3a24188a404dc31fa118c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/spatie/image/zipball/12662673fbe649bffcd3a24188a404dc31fa118c", + "reference": "12662673fbe649bffcd3a24188a404dc31fa118c", "shasum": "" }, "require": { - "php": ">=5.3.0" + "ext-exif": "*", + "ext-mbstring": "*", + "league/glide": "^1.6", + "php": "^7.2|^8.0", + "spatie/image-optimizer": "^1.1", + "spatie/temporary-directory": "^1.0", + "symfony/process": "^3.0|^4.0|^5.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^8.0|^9.0", + "symfony/var-dumper": "^4.0|^5.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\SimpleCache\\": "src/" + "Spatie\\Image\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1940,69 +4056,62 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "Common interfaces for simple caching", + "description": "Manipulate images with an expressive API", + "homepage": "https://github.com/spatie/image", "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" + "image", + "spatie" ], - "time": "2017-10-23T01:57:42+00:00" + "support": { + "issues": "https://github.com/spatie/image/issues", + "source": "https://github.com/spatie/image/tree/1.10.2" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2021-01-26T07:53:19+00:00" }, { - "name": "psy/psysh", - "version": "v0.10.6", + "name": "spatie/image-optimizer", + "version": "1.3.2", "source": { "type": "git", - "url": "https://github.com/bobthecow/psysh.git", - "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3" + "url": "https://github.com/spatie/image-optimizer.git", + "reference": "6aa170eb292758553d332efee5e0c3977341080c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6f990c19f91729de8b31e639d6e204ea59f19cf3", - "reference": "6f990c19f91729de8b31e639d6e204ea59f19cf3", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/6aa170eb292758553d332efee5e0c3977341080c", + "reference": "6aa170eb292758553d332efee5e0c3977341080c", "shasum": "" }, "require": { - "dnoegel/php-xdg-base-dir": "0.1.*", - "ext-json": "*", - "ext-tokenizer": "*", - "nikic/php-parser": "~4.0|~3.0|~2.0|~1.3", - "php": "^8.0 || ^7.0 || ^5.5.9", - "symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10", - "symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7" + "ext-fileinfo": "*", + "php": "^7.2|^8.0", + "psr/log": "^1.0", + "symfony/process": "^4.2|^5.0" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "hoa/console": "3.17.*" - }, - "suggest": { - "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", - "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.", - "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.", - "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit." + "phpunit/phpunit": "^8.0|^9.0", + "symfony/var-dumper": "^4.2|^5.0" }, - "bin": [ - "bin/psysh" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "0.10.x-dev" - } - }, "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Psy\\": "src/" + "Spatie\\ImageOptimizer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2011,47 +4120,87 @@ ], "authors": [ { - "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "description": "Easily optimize images using PHP", + "homepage": "https://github.com/spatie/image-optimizer", "keywords": [ - "REPL", - "console", - "interactive", - "shell" + "image-optimizer", + "spatie" ], - "time": "2021-01-18T15:53:43+00:00" + "support": { + "issues": "https://github.com/spatie/image-optimizer/issues", + "source": "https://github.com/spatie/image-optimizer/tree/1.3.2" + }, + "time": "2020-11-28T12:37:58+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "spatie/laravel-medialibrary", + "version": "8.10.1", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/spatie/laravel-medialibrary.git", + "reference": "72b408972ba0b994eb52f39d38169621699e549b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/72b408972ba0b994eb52f39d38169621699e549b", + "reference": "72b408972ba0b994eb52f39d38169621699e549b", "shasum": "" }, "require": { - "php": ">=5.6" + "ext-fileinfo": "*", + "ext-json": "*", + "illuminate/bus": "^6.18|^7.0|^8.0", + "illuminate/console": "^6.18|^7.0|^8.0", + "illuminate/database": "^6.18|^7.0|^8.0", + "illuminate/pipeline": "^6.18|^7.0|^8.0", + "illuminate/support": "^6.18|^7.0|^8.0", + "league/flysystem": "^1.0.64", + "maennchen/zipstream-php": "^1.0|^2.0", + "php": "^7.4", + "spatie/image": "^1.4.0", + "spatie/temporary-directory": "^1.1", + "symfony/console": "^4.4|^5.0" + }, + "conflict": { + "php-ffmpeg/php-ffmpeg": "<0.6.1" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "aws/aws-sdk-php": "^3.133.11", + "doctrine/dbal": "^2.5.2", + "ext-pdo_sqlite": "*", + "ext-zip": "*", + "guzzlehttp/guzzle": "^6.3|^7.0", + "league/flysystem-aws-s3-v3": "^1.0.23", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^4.0|^5.0|^6.0", + "php-ffmpeg/php-ffmpeg": "^0.16.0", + "phpunit/phpunit": "^9.1", + "spatie/pdf-to-image": "^2.0", + "spatie/phpunit-snapshot-assertions": "^4.0" + }, + "suggest": { + "league/flysystem-aws-s3-v3": "Required to use AWS S3 file storage", + "php-ffmpeg/php-ffmpeg": "Required for generating video thumbnails", + "spatie/pdf-to-image": "Required for generating thumbsnails of PDFs and SVGs" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\MediaLibrary\\MediaLibraryServiceProvider" + ] + } + }, "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "Spatie\\MediaLibrary\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2059,52 +4208,78 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Associate files with Eloquent models", + "homepage": "https://github.com/spatie/laravel-medialibrary", + "keywords": [ + "cms", + "conversion", + "downloads", + "images", + "laravel", + "laravel-medialibrary", + "media", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-medialibrary/issues", + "source": "https://github.com/spatie/laravel-medialibrary/tree/8.10.1" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" } ], - "description": "A polyfill for getallheaders.", - "time": "2019-03-08T08:55:37+00:00" + "time": "2020-10-05T21:54:57+00:00" }, { - "name": "ramsey/collection", - "version": "1.1.3", + "name": "spatie/laravel-query-builder", + "version": "3.3.4", "source": { "type": "git", - "url": "https://github.com/ramsey/collection.git", - "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1" + "url": "https://github.com/spatie/laravel-query-builder.git", + "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1", - "reference": "28a5c4ab2f5111db6a60b2b4ec84057e0f43b9c1", + "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", + "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", "shasum": "" }, "require": { - "php": "^7.2 || ^8" + "illuminate/database": "^6.0|^7.0|^8.0", + "illuminate/http": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "php": "^7.3|^8.0" }, "require-dev": { - "captainhook/captainhook": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "ergebnis/composer-normalize": "^2.6", - "fakerphp/faker": "^1.5", - "hamcrest/hamcrest-php": "^2", - "jangregor/phpstan-prophecy": "^0.8", - "mockery/mockery": "^1.3", - "phpstan/extension-installer": "^1", - "phpstan/phpstan": "^0.12.32", - "phpstan/phpstan-mockery": "^0.12.5", - "phpstan/phpstan-phpunit": "^0.12.11", - "phpunit/phpunit": "^8.5 || ^9", - "psy/psysh": "^0.10.4", - "slevomat/coding-standard": "^6.3", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.4" + "ext-json": "*", + "laravel/legacy-factories": "^1.0.4", + "mockery/mockery": "^1.4", + "orchestra/testbench": "^4.9|^5.8|^6.3", + "phpunit/phpunit": "^9.0" }, "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\QueryBuilder\\QueryBuilderServiceProvider" + ] + } + }, "autoload": { "psr-4": { - "Ramsey\\Collection\\": "src/" + "Spatie\\QueryBuilder\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2113,118 +4288,80 @@ ], "authors": [ { - "name": "Ben Ramsey", - "email": "ben@benramsey.com", - "homepage": "https://benramsey.com" + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "description": "A PHP 7.2+ library for representing and manipulating collections.", + "description": "Easily build Eloquent queries from API requests", + "homepage": "https://github.com/spatie/laravel-query-builder", "keywords": [ - "array", - "collection", - "hash", - "map", - "queue", - "set" + "laravel-query-builder", + "spatie" ], + "support": { + "issues": "https://github.com/spatie/laravel-query-builder/issues", + "source": "https://github.com/spatie/laravel-query-builder" + }, "funding": [ { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" + "url": "https://spatie.be/open-source/support-us", + "type": "custom" } ], - "time": "2021-01-21T17:40:04+00:00" + "time": "2020-11-26T14:51:30+00:00" }, { - "name": "ramsey/uuid", - "version": "4.1.1", + "name": "spatie/temporary-directory", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/ramsey/uuid.git", - "reference": "cd4032040a750077205918c86049aa0f43d22947" + "url": "https://github.com/spatie/temporary-directory.git", + "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/cd4032040a750077205918c86049aa0f43d22947", - "reference": "cd4032040a750077205918c86049aa0f43d22947", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/f517729b3793bca58f847c5fd383ec16f03ffec6", + "reference": "f517729b3793bca58f847c5fd383ec16f03ffec6", "shasum": "" }, "require": { - "brick/math": "^0.8 || ^0.9", - "ext-json": "*", - "php": "^7.2 || ^8", - "ramsey/collection": "^1.0", - "symfony/polyfill-ctype": "^1.8" - }, - "replace": { - "rhumsaa/uuid": "self.version" + "php": "^7.2|^8.0" }, "require-dev": { - "codeception/aspect-mock": "^3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7.0", - "doctrine/annotations": "^1.8", - "goaop/framework": "^2", - "mockery/mockery": "^1.3", - "moontoast/math": "^1.1", - "paragonie/random-lib": "^2", - "php-mock/php-mock-mockery": "^1.3", - "php-mock/php-mock-phpunit": "^2.5", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^0.17.1", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-mockery": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^8.5", - "psy/psysh": "^0.10.0", - "slevomat/coding-standard": "^6.0", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "3.9.4" - }, - "suggest": { - "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", - "ext-ctype": "Enables faster processing of character classification using ctype functions.", - "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", - "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", - "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", - "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + "phpunit/phpunit": "^8.0|^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, "autoload": { "psr-4": { - "Ramsey\\Uuid\\": "src/" - }, - "files": [ - "src/functions.php" - ] + "Spatie\\TemporaryDirectory\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "homepage": "https://github.com/ramsey/uuid", - "keywords": [ - "guid", - "identifier", - "uuid" - ], - "funding": [ + "authors": [ { - "url": "https://github.com/ramsey", - "type": "github" + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" } ], - "time": "2020-08-18T17:17:46+00:00" + "description": "Easily create, use and destroy temporary directories", + "homepage": "https://github.com/spatie/temporary-directory", + "keywords": [ + "php", + "spatie", + "temporary-directory" + ], + "support": { + "issues": "https://github.com/spatie/temporary-directory/issues", + "source": "https://github.com/spatie/temporary-directory/tree/1.3.0" + }, + "time": "2020-11-09T15:54:21+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -2285,6 +4422,10 @@ "mail", "mailer" ], + "support": { + "issues": "https://github.com/swiftmailer/swiftmailer/issues", + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5" + }, "funding": [ { "url": "https://github.com/fabpot", @@ -2375,6 +4516,9 @@ "console", "terminal" ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2437,6 +4581,9 @@ ], "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2501,6 +4648,9 @@ ], "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2567,6 +4717,9 @@ ], "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2649,6 +4802,9 @@ ], "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2725,6 +4881,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.2.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2783,6 +4942,9 @@ ], "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2859,6 +5021,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -2929,6 +5094,9 @@ ], "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3038,6 +5206,9 @@ ], "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3117,6 +5288,9 @@ "mime", "mime-type" ], + "support": { + "source": "https://github.com/symfony/mime/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3193,6 +5367,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3270,6 +5447,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3348,6 +5528,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3432,6 +5615,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3513,6 +5699,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3590,6 +5779,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3663,6 +5855,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3739,6 +5934,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3819,6 +6017,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3878,6 +6079,9 @@ ], "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -3965,6 +6169,9 @@ "uri", "url" ], + "support": { + "source": "https://github.com/symfony/routing/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4041,6 +6248,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4121,6 +6331,9 @@ "utf-8", "utf8" ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4211,6 +6424,9 @@ ], "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4286,6 +6502,9 @@ "interoperability", "standards" ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4371,6 +6590,9 @@ "debug", "dump" ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v5.2.3" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -4434,8 +6656,92 @@ ], "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/2.2.3" + }, "time": "2020-07-13T06:12:54+00:00" }, + { + "name": "torann/geoip", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/Torann/laravel-geoip.git", + "reference": "f16d5df66ecb6ba4ffaef52abef519fbc19596d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Torann/laravel-geoip/zipball/f16d5df66ecb6ba4ffaef52abef519fbc19596d3", + "reference": "f16d5df66ecb6ba4ffaef52abef519fbc19596d3", + "shasum": "" + }, + "require": { + "illuminate/cache": "^8.0", + "illuminate/console": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "geoip2/geoip2": "~2.1", + "mockery/mockery": "^1.3", + "phpstan/phpstan": "^0.12.14", + "phpunit/phpunit": "^8.0", + "squizlabs/php_codesniffer": "^3.5", + "vlucas/phpdotenv": "^5.0" + }, + "suggest": { + "geoip2/geoip2": "Required to use the MaxMind database or web service with GeoIP (~2.1).", + "monolog/monolog": "Allows for storing location not found errors to the log" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + }, + "laravel": { + "providers": [ + "Torann\\GeoIP\\GeoIPServiceProvider" + ], + "aliases": { + "GeoIP": "Torann\\GeoIP\\Facades\\GeoIP" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Torann\\GeoIP\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Daniel Stainback", + "email": "torann@gmail.com" + } + ], + "description": "Support for multiple GeoIP services.", + "keywords": [ + "IP API", + "geoip", + "geolocation", + "infoDB", + "laravel", + "location", + "maxmind" + ], + "support": { + "issues": "https://github.com/Torann/laravel-geoip/issues", + "source": "https://github.com/Torann/laravel-geoip/tree/3.0.2" + }, + "time": "2020-12-21T19:12:11+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.3.0", @@ -4500,6 +6806,10 @@ "env", "environment" ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.3.0" + }, "funding": [ { "url": "https://github.com/GrahamCampbell", @@ -4558,6 +6868,10 @@ "clean", "php" ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/1.5.6" + }, "funding": [ { "url": "https://www.paypal.me/moelleken", @@ -4629,6 +6943,10 @@ "check", "validate" ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.9.1" + }, "time": "2020-07-08T17:02:28+00:00" } ], @@ -4682,6 +7000,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, "funding": [ { "url": "https://www.doctrine-project.org/sponsorship.html", @@ -4751,6 +7073,10 @@ "flare", "reporting" ], + "support": { + "issues": "https://github.com/facade/flare-client-php/issues", + "source": "https://github.com/facade/flare-client-php/tree/1.4.0" + }, "funding": [ { "url": "https://github.com/spatie", @@ -4828,6 +7154,12 @@ "laravel", "page" ], + "support": { + "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", + "forum": "https://twitter.com/flareappio", + "issues": "https://github.com/facade/ignition/issues", + "source": "https://github.com/facade/ignition" + }, "time": "2021-02-16T12:46:19+00:00" }, { @@ -4877,6 +7209,10 @@ "flare", "ignition" ], + "support": { + "issues": "https://github.com/facade/ignition-contracts/issues", + "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" + }, "time": "2020-10-16T08:27:54+00:00" }, { @@ -4925,6 +7261,10 @@ "faker", "fixtures" ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0" + }, "time": "2020-12-18T16:50:48+00:00" }, { @@ -4986,6 +7326,10 @@ "throwable", "whoops" ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.9.2" + }, "funding": [ { "url": "https://github.com/denis-sokolov", @@ -5039,6 +7383,10 @@ "keywords": [ "test" ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, "time": "2020-07-09T08:09:16+00:00" }, { @@ -5095,6 +7443,10 @@ "docker", "laravel" ], + "support": { + "issues": "https://github.com/laravel/sail/issues", + "source": "https://github.com/laravel/sail" + }, "time": "2021-02-24T21:20:16+00:00" }, { @@ -5163,6 +7515,10 @@ "test double", "testing" ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.4.3" + }, "time": "2021-02-24T09:51:49+00:00" }, { @@ -5211,6 +7567,10 @@ "object", "object graph" ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, "funding": [ { "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", @@ -5287,6 +7647,10 @@ "php", "symfony" ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, "funding": [ { "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", @@ -5357,6 +7721,10 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/master" + }, "time": "2020-06-27T14:33:11+00:00" }, { @@ -5404,6 +7772,10 @@ } ], "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, "time": "2021-02-23T14:00:09+00:00" }, { @@ -5453,6 +7825,10 @@ "reflection", "static analysis" ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, "time": "2020-06-27T09:03:43+00:00" }, { @@ -5505,6 +7881,10 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + }, "time": "2020-09-03T19:13:55+00:00" }, { @@ -5550,6 +7930,10 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.4.0" + }, "time": "2020-09-17T18:55:26+00:00" }, { @@ -5613,6 +7997,10 @@ "spy", "stub" ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.12.2" + }, "time": "2020-12-19T10:15:11+00:00" }, { @@ -5680,6 +8068,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5736,6 +8128,10 @@ "filesystem", "iterator" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5795,6 +8191,10 @@ "keywords": [ "process" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5850,6 +8250,10 @@ "keywords": [ "template" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -5905,6 +8309,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6000,6 +8408,10 @@ "testing", "xunit" ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2" + }, "funding": [ { "url": "https://phpunit.de/donate.html", @@ -6056,6 +8468,10 @@ ], "description": "Library for parsing CLI options", "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6108,6 +8524,10 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6159,6 +8579,10 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6229,6 +8653,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6282,6 +8710,10 @@ ], "description": "Library for calculating the complexity of PHP code units", "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6344,6 +8776,10 @@ "unidiff", "unified diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6403,6 +8839,10 @@ "environment", "hhvm" ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6476,6 +8916,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6536,6 +8980,10 @@ "keywords": [ "global state" ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6589,6 +9037,10 @@ ], "description": "Library for counting the lines of code in PHP source code", "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6642,6 +9094,10 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6693,6 +9149,10 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6752,6 +9212,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6803,6 +9267,10 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6855,6 +9323,10 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6904,6 +9376,10 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -6950,6 +9426,10 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/master" + }, "funding": [ { "url": "https://github.com/theseer", @@ -6965,8 +9445,10 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.3|^8.0" + "php": "^7.3|^8.0", + "ext-gd": "*", + "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } diff --git a/config.default/app.php b/config.default/app.php new file mode 100644 index 0000000..2a2f0eb --- /dev/null +++ b/config.default/app.php @@ -0,0 +1,232 @@ + 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, + + ], + +]; diff --git a/config/auth.php b/config.default/auth.php similarity index 100% rename from config/auth.php rename to config.default/auth.php diff --git a/config/broadcasting.php b/config.default/broadcasting.php similarity index 100% rename from config/broadcasting.php rename to config.default/broadcasting.php diff --git a/config/cache.php b/config.default/cache.php similarity index 100% rename from config/cache.php rename to config.default/cache.php diff --git a/config.default/cors.php b/config.default/cors.php new file mode 100644 index 0000000..8a39e6d --- /dev/null +++ b/config.default/cors.php @@ -0,0 +1,34 @@ + ['api/*', 'sanctum/csrf-cookie'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/config/database.php b/config.default/database.php similarity index 100% rename from config/database.php rename to config.default/database.php diff --git a/config.default/filesystems.php b/config.default/filesystems.php new file mode 100644 index 0000000..10c9d9b --- /dev/null +++ b/config.default/filesystems.php @@ -0,0 +1,72 @@ + 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'), + ], + +]; diff --git a/config/hashing.php b/config.default/hashing.php similarity index 100% rename from config/hashing.php rename to config.default/hashing.php diff --git a/config.default/logging.php b/config.default/logging.php new file mode 100644 index 0000000..6aa77fe --- /dev/null +++ b/config.default/logging.php @@ -0,0 +1,104 @@ + 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'), + ], + ], + +]; diff --git a/config/mail.php b/config.default/mail.php similarity index 100% rename from config/mail.php rename to config.default/mail.php diff --git a/config/queue.php b/config.default/queue.php similarity index 100% rename from config/queue.php rename to config.default/queue.php diff --git a/config.default/services.php b/config.default/services.php new file mode 100644 index 0000000..2a1d616 --- /dev/null +++ b/config.default/services.php @@ -0,0 +1,33 @@ + [ + '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'), + ], + +]; diff --git a/config/session.php b/config.default/session.php similarity index 100% rename from config/session.php rename to config.default/session.php diff --git a/config/view.php b/config.default/view.php similarity index 100% rename from config/view.php rename to config.default/view.php diff --git a/config/amqp.php b/config/amqp.php new file mode 100644 index 0000000..d0734cf --- /dev/null +++ b/config/amqp.php @@ -0,0 +1,73 @@ + 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), + ], + ], + ], +]; diff --git a/config/app.php b/config/app.php index 2a2f0eb..1675900 100644 --- a/config/app.php +++ b/config/app.php @@ -1,232 +1,19 @@ 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 ]; diff --git a/config/cors.php b/config/cors.php index 8a39e6d..c3ded3c 100644 --- a/config/cors.php +++ b/config/cors.php @@ -1,34 +1,59 @@ ['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, - ]; diff --git a/config/filesystems.php b/config/filesystems.php index 10c9d9b..e5708dc 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -15,6 +15,19 @@ return [ 'default' => env('FILESYSTEM_DRIVER', 'local'), + /* + |-------------------------------------------------------------------------- + | Default Cloud Filesystem Disk + |-------------------------------------------------------------------------- + | + | Many applications store files both locally and in the cloud. For this + | reason, you may specify a default "cloud" driver here. This driver + | will be bound as the Cloud disk implementation in the container. + | + */ + + 'cloud' => env('FILESYSTEM_CLOUD', 's3'), + /* |-------------------------------------------------------------------------- | Filesystem Disks @@ -38,7 +51,7 @@ return [ 'public' => [ 'driver' => 'local', 'root' => storage_path('app/public'), - 'url' => env('APP_URL').'/storage', + 'url' => env('APP_URL') . '/storage', 'visibility' => 'public', ], @@ -46,8 +59,8 @@ return [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION'), - 'bucket' => env('AWS_BUCKET'), + 'region' => env('AWS_DEFAULT_REGION','us'), + 'bucket' => env('AWS_BUCKET','real-test-2'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), ], @@ -66,7 +79,7 @@ return [ */ 'links' => [ - public_path('storage') => storage_path('app/public'), + base_path('public/storage') => storage_path('app/public'), ], ]; diff --git a/config/geoip.php b/config/geoip.php new file mode 100644 index 0000000..0c812fc --- /dev/null +++ b/config/geoip.php @@ -0,0 +1,172 @@ + 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', + ], + +]; diff --git a/config/logging.php b/config/logging.php index 6aa77fe..31b0dcf 100644 --- a/config/logging.php +++ b/config/logging.php @@ -1,104 +1,44 @@ 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'), - ], ], - ]; diff --git a/config/media-library.php b/config/media-library.php new file mode 100644 index 0000000..6d21069 --- /dev/null +++ b/config/media-library.php @@ -0,0 +1,179 @@ + 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, + +]; diff --git a/config/query-builder.php b/config/query-builder.php new file mode 100644 index 0000000..b65680f --- /dev/null +++ b/config/query-builder.php @@ -0,0 +1,38 @@ + [ + '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, + +]; diff --git a/config/services.php b/config/services.php index 2a1d616..95b9b0a 100644 --- a/config/services.php +++ b/config/services.php @@ -1,33 +1,21 @@ [ - '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'), - ], - ]; diff --git a/database/factories/BusinessFactory.php b/database/factories/BusinessFactory.php new file mode 100644 index 0000000..8a15574 --- /dev/null +++ b/database/factories/BusinessFactory.php @@ -0,0 +1,19 @@ +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)), + ]; +}); diff --git a/database/factories/CostFactory.php b/database/factories/CostFactory.php new file mode 100644 index 0000000..649cc16 --- /dev/null +++ b/database/factories/CostFactory.php @@ -0,0 +1,19 @@ +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)), + ]; +}); diff --git a/database/factories/FileFactory.php b/database/factories/FileFactory.php new file mode 100644 index 0000000..c8a4620 --- /dev/null +++ b/database/factories/FileFactory.php @@ -0,0 +1,35 @@ +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, + ]; +}); diff --git a/database/factories/FingerprintFactory.php b/database/factories/FingerprintFactory.php new file mode 100644 index 0000000..bbd8a79 --- /dev/null +++ b/database/factories/FingerprintFactory.php @@ -0,0 +1,38 @@ +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), + ]; +}); diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php new file mode 100644 index 0000000..6dd5c09 --- /dev/null +++ b/database/factories/ProjectFactory.php @@ -0,0 +1,23 @@ +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, + ]; +}); diff --git a/database/factories/SprintflowFactory.php b/database/factories/SprintflowFactory.php new file mode 100644 index 0000000..ab520fb --- /dev/null +++ b/database/factories/SprintflowFactory.php @@ -0,0 +1,16 @@ +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, + ]; +}); diff --git a/database/factories/SystemFactory.php b/database/factories/SystemFactory.php new file mode 100644 index 0000000..0ea6223 --- /dev/null +++ b/database/factories/SystemFactory.php @@ -0,0 +1,16 @@ +define(System::class, function (Faker $faker) { + return [ + 'business_id' => null, + 'project_id' => null, + 'name' => $name = $faker->words(3, true), + ]; +}); diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php new file mode 100644 index 0000000..d6779b4 --- /dev/null +++ b/database/factories/TagFactory.php @@ -0,0 +1,15 @@ +define(Tag::class, function () use ($faker) { + return [ + 'label' => $faker->colorName, + 'color' => $faker->colorName, + 'business_id' => null, + ]; +}); diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php new file mode 100644 index 0000000..448b89e --- /dev/null +++ b/database/factories/TaskFactory.php @@ -0,0 +1,22 @@ +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(), + ]; +}); diff --git a/database/factories/TransactionFactory.php b/database/factories/TransactionFactory.php new file mode 100644 index 0000000..a2f346b --- /dev/null +++ b/database/factories/TransactionFactory.php @@ -0,0 +1,21 @@ +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)), + ]; +}); diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 3510ed6..871ca9d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -1,47 +1,16 @@ $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', + ]; +}); diff --git a/database/factories/WorkflowFactory.php b/database/factories/WorkflowFactory.php new file mode 100644 index 0000000..72cde2f --- /dev/null +++ b/database/factories/WorkflowFactory.php @@ -0,0 +1,15 @@ +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), + ]; +}); diff --git a/database/factories/WorkstatusFactory.php b/database/factories/WorkstatusFactory.php new file mode 100644 index 0000000..3d2f791 --- /dev/null +++ b/database/factories/WorkstatusFactory.php @@ -0,0 +1,15 @@ +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), + ]; +}); diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php deleted file mode 100644 index 6aa6d74..0000000 --- a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php +++ /dev/null @@ -1,36 +0,0 @@ -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'); - } -} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2020_08_18_085016_create_users_table.php similarity index 62% rename from database/migrations/2014_10_12_000000_create_users_table.php rename to database/migrations/2020_08_18_085016_create_users_table.php index 621a24e..b656a71 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2020_08_18_085016_create_users_table.php @@ -17,10 +17,14 @@ class CreateUsersTable extends Migration $table->id(); $table->string('name'); $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); + $table->string('mobile')->unique()->nullable(); + $table->string('username')->unique(); $table->string('password'); - $table->rememberToken(); - $table->timestamps(); + $table->boolean('active')->default(false); + $table->boolean('has_avatar')->default(false); + $table->timestamp('created_at')->nullable(); + $table->timestamp('updated_at')->useCurrent(); + $table->timestamp('deleted_at')->nullable(); }); } diff --git a/database/migrations/2020_08_18_085017_fingerprints.php b/database/migrations/2020_08_18_085017_fingerprints.php new file mode 100644 index 0000000..67bd1ee --- /dev/null +++ b/database/migrations/2020_08_18_085017_fingerprints.php @@ -0,0 +1,38 @@ +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'); + } +} diff --git a/database/migrations/2020_08_18_085018_create_businesses_table.php b/database/migrations/2020_08_18_085018_create_businesses_table.php new file mode 100644 index 0000000..e37d713 --- /dev/null +++ b/database/migrations/2020_08_18_085018_create_businesses_table.php @@ -0,0 +1,49 @@ +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'); + } +} diff --git a/database/migrations/2020_08_18_085046_create_projects_table.php b/database/migrations/2020_08_18_085046_create_projects_table.php new file mode 100644 index 0000000..4b40f42 --- /dev/null +++ b/database/migrations/2020_08_18_085046_create_projects_table.php @@ -0,0 +1,58 @@ +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'); + } +} diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2020_08_18_085054_create_workflows_table.php similarity index 53% rename from database/migrations/2014_10_12_100000_create_password_resets_table.php rename to database/migrations/2020_08_18_085054_create_workflows_table.php index 0ee0a36..d998c40 100644 --- a/database/migrations/2014_10_12_100000_create_password_resets_table.php +++ b/database/migrations/2020_08_18_085054_create_workflows_table.php @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -class CreatePasswordResetsTable extends Migration +class CreateWorkflowsTable extends Migration { /** * Run the migrations. @@ -13,10 +13,13 @@ class CreatePasswordResetsTable extends Migration */ public function up() { - Schema::create('password_resets', function (Blueprint $table) { - $table->string('email')->index(); - $table->string('token'); + Schema::create('workflows', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('business_id'); + $table->string('name'); + $table->string('desc')->nullable(); $table->timestamp('created_at')->nullable(); + $table->timestamp('updated_at')->useCurrent(); }); } @@ -27,6 +30,6 @@ class CreatePasswordResetsTable extends Migration */ public function down() { - Schema::dropIfExists('password_resets'); + Schema::dropIfExists('workflows'); } } diff --git a/database/migrations/2020_08_18_085102_create_statuses_table.php b/database/migrations/2020_08_18_085102_create_statuses_table.php new file mode 100644 index 0000000..03c4203 --- /dev/null +++ b/database/migrations/2020_08_18_085102_create_statuses_table.php @@ -0,0 +1,39 @@ +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'); + } +} diff --git a/database/migrations/2020_08_18_085114_create_tags_table.php b/database/migrations/2020_08_18_085114_create_tags_table.php new file mode 100644 index 0000000..39a8e9b --- /dev/null +++ b/database/migrations/2020_08_18_085114_create_tags_table.php @@ -0,0 +1,41 @@ +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'); + } +} diff --git a/database/migrations/2020_08_28_095802_create_systems_table.php b/database/migrations/2020_08_28_095802_create_systems_table.php new file mode 100644 index 0000000..32fbb14 --- /dev/null +++ b/database/migrations/2020_08_28_095802_create_systems_table.php @@ -0,0 +1,34 @@ +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'); + } +} diff --git a/database/migrations/2020_08_28_101545_create_sprint_table.php b/database/migrations/2020_08_28_101545_create_sprint_table.php new file mode 100644 index 0000000..9a73d99 --- /dev/null +++ b/database/migrations/2020_08_28_101545_create_sprint_table.php @@ -0,0 +1,38 @@ +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'); + } +} diff --git a/database/migrations/2020_10_31_182018_create_transactions_table.php b/database/migrations/2020_10_31_182018_create_transactions_table.php new file mode 100644 index 0000000..5fa18e5 --- /dev/null +++ b/database/migrations/2020_10_31_182018_create_transactions_table.php @@ -0,0 +1,37 @@ +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'); + } +} diff --git a/database/migrations/2021_09_03_085114_create_costs_table.php b/database/migrations/2021_09_03_085114_create_costs_table.php new file mode 100644 index 0000000..87768ec --- /dev/null +++ b/database/migrations/2021_09_03_085114_create_costs_table.php @@ -0,0 +1,41 @@ +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'); + } +} diff --git a/database/migrations/2022_10_13_085114_create_files_table.php b/database/migrations/2022_10_13_085114_create_files_table.php new file mode 100644 index 0000000..69dfec8 --- /dev/null +++ b/database/migrations/2022_10_13_085114_create_files_table.php @@ -0,0 +1,45 @@ +id(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('business_id'); + $table->unsignedBigInteger('project_id'); + $table->unsignedBigInteger('attached_to_id')->nullable(); + $table->integer('attached_to_table')->nullable(); + $table->char('disk',191); + $table->char('original_name',191); + $table->char('name',191); + $table->char('extension',4); + $table->string('mime'); + $table->string('group',255); + $table->char('size',191); + $table->text('description')->nullable(); + $table->timestamp('created_at')->nullable(); + $table->timestamp('updated_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('files'); + } +} diff --git a/database/migrations/2023_10_13_085115_create_files_relation_table.php b/database/migrations/2023_10_13_085115_create_files_relation_table.php new file mode 100644 index 0000000..8308d77 --- /dev/null +++ b/database/migrations/2023_10_13_085115_create_files_relation_table.php @@ -0,0 +1,35 @@ +id(); + $table->unsignedBigInteger('file_id'); + $table->unsignedBigInteger("resource_id"); + $table->string("resource_type"); + // the person who create this relation. eg attacher + $table->unsignedBigInteger('user_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('files_relation'); + } +} diff --git a/database/migrations/2023_10_14_085115_create_media_table.php.php b/database/migrations/2023_10_14_085115_create_media_table.php.php new file mode 100644 index 0000000..d0ac271 --- /dev/null +++ b/database/migrations/2023_10_14_085115_create_media_table.php.php @@ -0,0 +1,31 @@ +bigIncrements('id'); + + $table->morphs('model'); + $table->uuid('uuid')->nullable(); + $table->string('collection_name'); + $table->string('name'); + $table->string('file_name'); + $table->string('mime_type')->nullable(); + $table->string('disk'); + $table->string('conversions_disk')->nullable(); + $table->unsignedBigInteger('size'); + $table->json('manipulations'); + $table->json('custom_properties'); + $table->json('responsive_images'); + $table->unsignedInteger('order_column')->nullable(); + + $table->nullableTimestamps(); + }); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php deleted file mode 100644 index 57b73b5..0000000 --- a/database/seeders/DatabaseSeeder.php +++ /dev/null @@ -1,18 +0,0 @@ -create(); - } -} diff --git a/database/seeds/BusinessSeeder.php b/database/seeds/BusinessSeeder.php new file mode 100644 index 0000000..f622594 --- /dev/null +++ b/database/seeds/BusinessSeeder.php @@ -0,0 +1,144 @@ +raw()); + $projects = []; + $systems = []; + $business_user = []; + $project_user = []; + $project_id = 0; + for ($business_id=1; $business_id <= 2000; $business_id++) { + // distinct user ids + $user_ids = []; + $added_user = []; + for ($i=0; $i < 10; $i++) $user_ids [] = rand(1, 5000); + $user_ids = array_values(array_unique($user_ids)); + // add 1 to 7 member + for ($i=0; $i < rand(1, 8); $i++) { + $added_user[] = $user_ids[$i]; + $business_user[] = [ + 'business_id' => $business_id, + 'user_id' => $user_ids[$i], + 'level' => $i == 0? 4: rand(0, 4), + ]; + } + + $business_projects = factory(Project::class, rand(1, 3))->raw(); + foreach ($business_projects as $project) { + $project['business_id'] = $business_id; + $projects[] = $project; + $project_id++; + + for ($i=0; $i < rand(0, 2); $i++) { + if ($added_user[$i] ?? false) { + $project_user[] = [ + 'project_id' => $project_id, + 'user_id' => $added_user[$i], + 'level' => rand(0, 4), + ]; + } + } + + for ($i=0; $i < rand(1, 5); $i++) { + $systems[] = factory(System::class)->raw([ + 'project_id' => $project_id, + 'business_id' => $business_id, + ]); + } + } + + // for ($i=0; $i < rand(1, 3); $i++) { + // // $project = + // } + // $business_user[] = [ + // 'business_id' => $business_id, + // 'user_id' => 1, + // 'owner' => true, + // ]; + } + DB::table('business_user')->insert($business_user); + Project::insert($projects); + DB::table('project_user')->insert($project_user); + DB::table('systems')->insert($systems); + /** + * Every business has one or more owners, which might be the owner of other business as well + */ + // $this->insertAdmins($business_ids, $owners); + + + /** + * Every business has one or more members, which might be the member of other business as well + */ + // $this->insertMembers($business_ids); + } + + /** + * @param $business_ids + * @param Collection $owners + * @return array + */ + public function insertAdmins($business_ids, Collection $owners) + { + $relations = []; + foreach ($business_ids as $id) { + $relations[] = [ + 'business_id' => $id, + 'user_id' => $owners->random()->id, + 'owner' => true, + ]; + } + + // Specify the percentage of managers who have two or more businesses + $multiple = ceil(count($relations) / 100 * 30); + foreach (Arr::random($relations, $multiple) as $relation) { + $relations[] = [ + 'business_id' => $relation['business_id'], + 'user_id' => $owners->except($relation['user_id'])->random()->id, + 'owner' => true, + ]; + } + + DB::table('business_user')->insert($relations); + } + + /** + * @param $business_ids + */ + public function insertMembers($business_ids): void + { + /** @var Collection $member_ids */ + $member_ids = User::whereDoesntHave('businesses')->select('id')->pluck('id'); + $relations = []; + $current = []; + foreach ($member_ids as $index => $id) { + $relations[] = [ + 'business_id' => $business = $business_ids->random(), + 'user_id' => $id, + ]; + + $current[$business][] = $index; + } + + $multiple = ceil(count($relations) / 100 * 20); + foreach (Arr::random($relations, $multiple) as $relation) { + $relations[] = [ + 'business_id' => $relation['business_id'], + 'user_id' => $member_ids->except($current[$relation['business_id']])->random(), + ]; + } + + DB::table('business_user')->insert($relations); + } +} diff --git a/database/seeds/CostSeeder.php b/database/seeds/CostSeeder.php new file mode 100644 index 0000000..5513efa --- /dev/null +++ b/database/seeds/CostSeeder.php @@ -0,0 +1,14 @@ +raw() + ); + } +} diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php new file mode 100644 index 0000000..959a0d6 --- /dev/null +++ b/database/seeds/DatabaseSeeder.php @@ -0,0 +1,24 @@ +raw()); + $this->call([ + UserSeeder::class, + BusinessSeeder::class, + TagSeeder::class, + WorkflowSeeder::class, + SprintSeeder::class, + TransactionSeeder::class, + CostSeeder::class, + // ProjectSeeder::class, + // TaskSeeder::class, + // FileSeeder::class + ]); + } +} diff --git a/database/seeds/FileSeeder.php b/database/seeds/FileSeeder.php new file mode 100644 index 0000000..e5f927a --- /dev/null +++ b/database/seeds/FileSeeder.php @@ -0,0 +1,13 @@ +raw(); + } +} diff --git a/database/seeds/ProjectSeeder.php b/database/seeds/ProjectSeeder.php new file mode 100644 index 0000000..5c91c26 --- /dev/null +++ b/database/seeds/ProjectSeeder.php @@ -0,0 +1,123 @@ +pluck('id'); + + /** + * One level projects + */ + $this->insertMainProjects($businesses); + + /** + * Multi level projects + * Be careful with private projects + */ + $this->insertChildProjects(); + + $this->insertGrandChildrenProjects(); + + /** + * Assign people to the projects + * * Be careful with private projects + */ + $this->assignPeopleToProjects(); + } + + /** + * @param $businesses + * @return void + */ + public function insertMainProjects($businesses): void + { + $projects = []; + foreach ($businesses as $business) { + $projects[] = factory(Project::class, 5)->raw([ + 'business_id' => $business, + 'private' => rand(1, 100) <= 10, + ]); + } + DB::table('projects')->insertOrIgnore(Arr::collapse($projects)); + } + + /** + * @return void + */ + public function insertChildProjects(): void + { + $projects = Project::inRandomOrder()->select('id', 'business_id')->take(100)->get()->toArray(); + $child_projects = []; + foreach ($projects as $project) { + $child_projects[] = factory(Project::class)->raw([ + 'parent_id' => $project['id'], + 'business_id' => $project['business_id'], + 'private' => rand(1, 100) <= 10, + ]); + } + DB::table('projects')->insert($child_projects); + } + + public function insertGrandChildrenProjects(): void + { + $projects = Project::inRandomOrder() + ->select('id', 'business_id', 'parent_id') + ->whereNotNull('parent_id') + ->take(25)->get()->toArray(); + $grand_child_projects = []; + foreach ($projects as $project) { + $grand_child_projects[] = factory(Project::class)->raw([ + 'parent_id' => $project['id'], + 'business_id' => $project['business_id'], + 'private' => rand(1, 100) <= 10, + ]); + } + DB::table('projects')->insertOrIgnore($grand_child_projects); + } + + public function assignPeopleToProjects(): void + { + // Schema::create('') + // $table->unsignedBigInteger('project_id'); + // $table->unsignedBigInteger(''); + + $project_members = []; + $projects = Project::with('parent','children','business')->get(); + foreach ($projects as $project) { + // detect the main projects + if ($project->children->isNotEmpty()) { + continue; + } + + $business_owner = $project->business->owners->random(); + $business_members = $project->business->members->random($project->business->members->count() - 1); + $project_members[] = [ + 'project_id' => $project->id, + 'user_id' => $business_owner->id, + ]; + + foreach ($business_members as $business_member) { + $project_members[] = [ + 'project_id' => $project->id, + 'user_id' => $business_member->id, + ]; + } + + // todo : how to detect a project has sub project + // child + // grand child + } + DB::table('project_user')->insert($project_members); + } +} diff --git a/database/seeds/SprintSeeder.php b/database/seeds/SprintSeeder.php new file mode 100644 index 0000000..6094e9a --- /dev/null +++ b/database/seeds/SprintSeeder.php @@ -0,0 +1,21 @@ +raw([ + 'business_id' => $business_id, + 'project_id' => rand(1, 4027), + ]); + } + } + DB::table('sprints')->insertOrIgnore($sprints); + } +} diff --git a/database/seeds/TagSeeder.php b/database/seeds/TagSeeder.php new file mode 100644 index 0000000..c15eb0e --- /dev/null +++ b/database/seeds/TagSeeder.php @@ -0,0 +1,22 @@ +raw([ + 'business_id' => $business_id, + ]); + } + } + DB::table('tags')->insertOrIgnore($tags); + } +} diff --git a/database/seeds/TaskSeeder.php b/database/seeds/TaskSeeder.php new file mode 100644 index 0000000..9b148bd --- /dev/null +++ b/database/seeds/TaskSeeder.php @@ -0,0 +1,62 @@ +get(); + $tasks = []; + + $left = count($businesses); + $now = Carbon::now()->toDateTimeString(); + $future = Carbon::now()->addDays(10)->toDateTimeString(); + foreach ($businesses as $business) { + foreach ($business->owners as $owner) { + foreach ($business->projects as $project) { + foreach ($business->members as $member) { + for ($i = 1; $i <= 30; $i++) { + foreach ($business->workflows as $workflow) { + $status = $workflow->workstatuses->random(); + $tasks[] = [ + 'business_id' => $business->id, +// 'creator_id' => $owner->id, +// 'project_id' => $project->id, +// 'user_id' => $member->id, +// 'workflow_id' => $workflow->id, +// 'work_status_id' => $status->id, +// 'name' => 'Task #' . rand(111111, 999999), +// 'time' => "10:00:00", +// 'cost' => 10000, +// 'completed' => $status->done, +// 'due_date' => $status->done ? $now : $future, + ]; + } + } + } + } + } + + $this->command->info(--$left . " Business is left."); + } + + dd('here'); + $chunks = array_chunk($tasks, 1000); + $number = count($chunks); + foreach ($chunks as $value) { + DB::table('tasks')->insert($value); + $this->command->info(--$number . " Chunk is left."); + } + } +} diff --git a/database/seeds/TransactionSeeder.php b/database/seeds/TransactionSeeder.php new file mode 100644 index 0000000..a25d5d8 --- /dev/null +++ b/database/seeds/TransactionSeeder.php @@ -0,0 +1,14 @@ +raw() + ); + } +} diff --git a/database/seeds/UserSeeder.php b/database/seeds/UserSeeder.php new file mode 100644 index 0000000..c3e67d9 --- /dev/null +++ b/database/seeds/UserSeeder.php @@ -0,0 +1,20 @@ +raw() + ); + Fingerprint::insert( + factory(Fingerprint::class, 5000)->raw() + ); + User::where('id', 1)->update(['mobile' => '09123456789']); + } +} diff --git a/database/seeds/WorkflowSeeder.php b/database/seeds/WorkflowSeeder.php new file mode 100644 index 0000000..893b58c --- /dev/null +++ b/database/seeds/WorkflowSeeder.php @@ -0,0 +1,51 @@ +raw([ + 'business_id' => $business_id, + ]); + $business_workflows[] = ++$workflow_inc; + for ($i=0; $i < rand(1, 4); $i++) { + $statuses[] = factory(Status::class)->raw([ + 'business_id' => $business_id, + 'workflow_id' => $workflow_inc, + ]); + $status_inc++; + } + } +// for ($i=0; $i < rand(1, 4); $i++) { +// $statuses[] = factory(Status::class)->raw([ +// 'business_id' => $business_id, +// ]); +// $status_inc++; +// for ($ws=0; $ws < rand(0, count($business_workflows)); $ws++) { +// $status_workflow[] = [ +// 'status_id' => $status_inc, +// 'workflow_id' => $business_workflows[rand(0, count($business_workflows)-1)], +// 'order' => rand(0, 10) +// ]; +// } +// } + } + DB::table('workflows')->insert($workflows); + DB::table('statuses')->insert($statuses); +// DB::table('status_workflow')->insert($status_workflow); + } +} diff --git a/routes/api.php b/routes/api.php index bcb8b18..618384b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,19 +1,165 @@ get('/user', function (Request $request) { - return $request->user(); +/** @var \Laravel\Lumen\Routing\Router $router */ + +$router->group(['prefix' => 'actions'], function () use ($router) { + $router->group(['prefix' => 'businesses'], function () use ($router) { + $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/info', 'BusinessController@info'); + }); + }); +}); + +$router->group(['prefix' => 'auth'], function () use ($router) { + $router->get('/', 'AuthController@auth'); + $router->delete('/', 'AuthController@delete'); + $router->get('/info', 'AuthController@authWithInfo'); + $router->post('login', 'AuthController@login'); + $router->post('logout', 'AuthController@logout'); + $router->post('register', 'AuthController@register'); + $router->post('revoke/{token}', 'AuthController@revoke'); + + $router->post('forget-password', 'AuthController@forgetPassword'); + $router->post('update-password', 'AuthController@updatePassword'); + + $router->post('verification', 'AuthController@verification'); + + $router->get('google/redirect', 'AuthController@redirectToGoogle'); + $router->get('google/callback', 'AuthController@handleGoogleCallback'); +}); + + + +$router->group(['prefix' => 'businesses'], function () use ($router) { + $router->get('/', 'BusinessController@index'); + $router->post('/', 'BusinessController@store'); + + $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) { + + + $router->put('/avatar', 'BusinessController@setAvatar'); + $router->delete('/avatar', 'BusinessController@unSetAvatar'); + + $router->get('/', 'BusinessController@show'); + $router->put('/', 'BusinessController@update'); + $router->delete('/', 'BusinessController@delete'); + $router->get('/restore', 'BusinessController@restore'); + + $router->get('/files', 'FileController@index'); + $router->get('/info', 'BusinessController@info'); + $router->get('/invoices', 'InvoiceController@index'); + $router->get('/invoices/{date}', 'InvoiceController@show'); + $router->get('/pay', 'CreditController@pay'); + $router->get('/payments', 'CreditController@payments'); + + + $router->group(['prefix' => 'projects'], function () use ($router) { + $router->get('/', 'ProjectController@index'); + $router->post('/', 'ProjectController@store'); + $router->group(['prefix' => '{project}'], function () use ($router) { + + $router->put('/avatar', 'ProjectController@setAvatar'); + $router->delete('/avatar', 'ProjectController@unSetAvatar'); + + $router->get('/', 'ProjectController@show'); + $router->put('/', 'ProjectController@update'); + $router->delete('/', 'ProjectController@delete'); + $router->group(['prefix' => 'files', 'as' => 'file.', 'middleware' => 'bindBusiness'], function () use ($router) { + // $router->get('/', ['as' => 'index', 'uses' => 'FileController@index']); + $router->post('/', ['as' => 'store', 'uses' => 'FileController@store']); + $router->group(['prefix' => '{file}'], function () use ($router) { + $router->get('/', ['as' => 'download', 'uses' => 'FileController@download']); + $router->put('/', ['as' => 'rename', 'uses' => 'FileController@rename']); + $router->delete('/', ['as' => 'delete', 'uses' => 'FileController@delete']); + }); + }); + $router->get('/restore', 'ProjectController@restore'); + + $router->group(['prefix' => 'sprints', 'as' => 'sprint.', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', ['as' => 'index', 'uses' => 'SprintController@index']); + $router->post('/', ['as' => 'store', 'uses' => 'SprintController@store']); + $router->group(['prefix' => '{sprint}'], function () use ($router) { + $router->get('/', ['as' => 'show', 'uses' => 'SprintController@show']); + $router->put('/', ['as' => 'update', 'uses' => 'SprintController@update']); + $router->delete('/', ['as' => 'delete', 'uses' => 'SprintController@delete']); + }); + }); + + $router->group(['prefix' => 'systems', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', 'SystemController@index'); + $router->post('/', 'SystemController@store'); + $router->group(['prefix' => '{system}'], function () use ($router) { + $router->get('/', 'SystemController@show'); + $router->put('/', 'SystemController@update'); + $router->delete('/', 'SystemController@delete'); + }); + }); + + $router->group(['prefix' => 'tasks/{task}/files', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', ['uses' => 'TaskFileController@index']); + $router->post('/', ['uses' => 'TaskFileController@sync']); + $router->get('/{file}', ['uses' => 'TaskFileController@download']); + }); + + $router->group(['prefix' => 'users'], function () use ($router){ + $router->post('/', 'ProjectController@storeOrUpdateUser'); + $router->group(['prefix' => '{user}'], function () use ($router) { + $router->delete('/', 'ProjectController@deleteUser'); + }); + }); + + }); + }); + + $router->group(['prefix' => 'tags', 'as' => 'tag.', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', ['as' => 'index', 'uses' => 'TagController@index']); + $router->post('/', ['as' => 'store', 'uses' => 'TagController@store']); + $router->group(['prefix' => '{tag}'], function () use ($router) { + $router->get('/', ['as' => 'show', 'uses' => 'TagController@show']); + $router->put('/', ['as' => 'update', 'uses' => 'TagController@update']); + $router->delete('/', ['as' => 'delete', 'uses' => 'TagController@delete']); + }); + }); + + $router->group(['prefix' => 'workflows', 'as' => 'workflow.', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', ['as' => 'index', 'uses' => 'WorkflowController@index']); + $router->post('/', ['as' => 'store', 'uses' => 'WorkflowController@store']); + $router->group(['prefix' => '{workflow}'], function () use ($router) { + $router->get('/', ['as' => 'show', 'uses' => 'WorkflowController@show']); + $router->put('/', ['as' => 'update', 'uses' => 'WorkflowController@update']); + $router->delete('/', ['as' => 'delete', 'uses' => 'WorkflowController@delete']); + $router->group(['prefix' => 'statuses', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/', 'StatusController@index'); + $router->post('/', 'StatusController@store'); + $router->group(['prefix' => '{status}'], function () use ($router) { + $router->get('/', 'StatusController@show'); + $router->put('/', 'StatusController@update'); + $router->delete('/', ['as' => 'delete', 'uses' => 'StatusController@delete']); + }); + }); + }); + }); + + $router->group(['prefix' => 'users'], function () use ($router){ + $router->post('/', 'BusinessController@storeOrUpdateUser'); + $router->group(['prefix' => '{user}'], function () use ($router) { + $router->delete('/', 'BusinessController@deleteUser'); + }); + }); + }); +}); + +$router->get('/callback', 'CreditController@callback'); +$router->get('/{transaction}/redirection', 'CreditController@redirection'); + +$router->group(['prefix' => 'users'], function () use ($router) { + $router->get('/', 'UserController@index'); + $router->get('/search', 'UserController@search'); + $router->group(['prefix' => '{user}'], function () use ($router) { + $router->get('/', 'UserController@show'); + $router->put('/', 'UserController@update'); + + $router->put('/avatar', 'UserController@setAvatar'); + $router->delete('/avatar', 'UserController@unSetAvatar'); + }); }); From 3adaad5066512e64f61a49811facd9ff95d80735 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 14:44:56 +0330 Subject: [PATCH 07/51] statistic directory, TagTask model, change factory, seeder, api --- app/Models/TagTask.php | 14 ++ app/Statists/Pipe.php | 8 ++ app/Statists/SprintStatist.php | 44 +++++++ app/Statists/SystemStatist.php | 22 ++++ app/Statists/TagStatist.php | 54 ++++++++ database/factories/TaskFactory.php | 32 +++-- database/factories/WorkFactory.php | 23 ++++ database/seeds/TaskSeeder.php | 204 ++++++++++++++++++++++------- database/seeds/WorkSeeder.php | 36 +++++ routes/api.php | 42 ++++++ 10 files changed, 422 insertions(+), 57 deletions(-) create mode 100644 app/Models/TagTask.php create mode 100644 app/Statists/Pipe.php create mode 100644 app/Statists/SprintStatist.php create mode 100644 app/Statists/SystemStatist.php create mode 100644 app/Statists/TagStatist.php create mode 100644 database/factories/WorkFactory.php mode change 100644 => 100755 database/seeds/TaskSeeder.php create mode 100755 database/seeds/WorkSeeder.php diff --git a/app/Models/TagTask.php b/app/Models/TagTask.php new file mode 100644 index 0000000..197f403 --- /dev/null +++ b/app/Models/TagTask.php @@ -0,0 +1,14 @@ +task = $task; + } + + public function handle(&$result, $next) + { + $key = 'sprints.'; + $key .= ($this->task->sprint_id ?? 0).'.'; + $key .= ($this->task->assignee_id ?? 0).'.'; + $key .= $this->task->workflow_id . '.'; + $key .= $this->task->status_id; + + $map = [ + 'total' => 0, + 'test' => 0, + 'overdue' => 0, + 'worked' => 0, // total worked in minute + 'estimate' => 0, // estimate total + ]; + + $node = Arr::get($result, $key, $map); + + $node['total'] = $node['total'] + 1; + $node['test'] = $node['test'] + $result->ready_to_test; + $node['overdue'] = $node['overdue'] + ($result->on_time? 0 : 1); + + Arr::set($result, $key, $node); + + return $next($result); + } +} diff --git a/app/Statists/SystemStatist.php b/app/Statists/SystemStatist.php new file mode 100644 index 0000000..c268aa3 --- /dev/null +++ b/app/Statists/SystemStatist.php @@ -0,0 +1,22 @@ +system_id ?? 0) . '.' . ($task->assignee_id ?? 0) . + '.' . $task->workflow_id . '.' . $task->status_id; + $node = Arr::get($result, $key, ['total' => 0, 'test' => 0, 'overdue' => 0]); + $node['total'] = $node['total'] + 1; + $node['test'] = $node['test'] + $task->ready_to_test; + $node['overdue'] = $node['overdue'] + ($task->on_time? 0: 1); + Arr::set($result, $key, $node); + + return $next($tasks); + } +} diff --git a/app/Statists/TagStatist.php b/app/Statists/TagStatist.php new file mode 100644 index 0000000..33bd15f --- /dev/null +++ b/app/Statists/TagStatist.php @@ -0,0 +1,54 @@ +first()->project_id; + + $tags = $tasks->pluck('tag_id'); + $users = $tasks->pluck('assignee_id'); + $workflows = $tasks->pluck('workflow_id'); + $statuses = $tasks->pluck('status_id'); + + foreach ($tags as $tag) { + foreach ($users as $user) { + foreach ($workflows as $workflow) { + foreach ($statuses as $status) { + $filter = $tasks + ->where('sprint_id', '=', $tag) + ->where('assignee_id', '=', $user) + ->where('workflow_id', '=', $workflow) + ->where('status_id', '=', $status); + + if ($filter->isEmpty()) { + continue; + } + + $stat = [ + 'task_count' => $filter->count(), + 'total_worked_in_minute' => $filter->sum('spent_time'), + 'overdue_count' => $filter->where('on_time','=',false)->count(), + 'total_estimate_in_minute' => $filter->sum('estimated_time'), + 'test_flag_count' => $filter->where('ready_to_test', '=', true)->count(), + ]; + + Arr::set($result,"{$tag}.{$user}.{$workflow}.{$status}",$stat); + } + } + } + } + + + Cache::put($project, $result); + + return $next($tasks); + } +} diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php index 448b89e..713b2cf 100644 --- a/database/factories/TaskFactory.php +++ b/database/factories/TaskFactory.php @@ -3,20 +3,30 @@ /** @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(), + 'business_id' => $faker->numberBetween(1, 200), + 'creator_id' => $faker->numberBetween(1, 5000), + 'project_id' => $faker->numberBetween(1, 200), + 'assignee_id' => $faker->numberBetween(1, 20), + 'workflow_id' => $faker->numberBetween(1, 2500), + 'system_id' => $faker->numberBetween(1, 20), + 'sprint_id' => $faker->numberBetween(1, 20), + 'status_id' => $faker->numberBetween(1, 5000), + 'approver_id' => $faker->numberBetween(1, 5000), + + 'title' => $faker->name, + 'description' => $faker->sentences(3, true), + 'priority' => $faker->numberBetween(1,10), + 'on_time' => $faker->boolean, + 'ready_to_test' => $faker->boolean, + 'spent_time' => $faker->numberBetween(100, 300), + 'estimated_time' => $faker->numberBetween(100, 300), + 'due_date' => $faker->date(), + 'completed_at' => $faker->date(), + 'work_start' => $faker->date('1971-01-01','now'), + 'work_finish' => $faker->date('1971-01-01','now'), ]; }); diff --git a/database/factories/WorkFactory.php b/database/factories/WorkFactory.php new file mode 100644 index 0000000..d4e9e2c --- /dev/null +++ b/database/factories/WorkFactory.php @@ -0,0 +1,23 @@ +define(Work::class, function (Faker $faker) { + $started_at = $faker->dateTime('now'); + $ended_at = $faker->dateTime('now'); +// $minute_sum = $ended_at->diffInMinutes($started_at); + return [ + 'business_id' => $faker->numberBetween(1, 200), + 'project_id' => $faker->numberBetween(1, 200), + 'task_id' => $faker->numberBetween(1, 200), + 'user_id' => $faker->numberBetween(1, 5000), + 'message' => $faker->name, + 'minute_sum' => $faker->numberBetween(100, 5000), + 'started_at' => $started_at, + 'ended_at' => $ended_at, + ]; +}); diff --git a/database/seeds/TaskSeeder.php b/database/seeds/TaskSeeder.php old mode 100644 new mode 100755 index 9b148bd..f138ee0 --- a/database/seeds/TaskSeeder.php +++ b/database/seeds/TaskSeeder.php @@ -1,62 +1,174 @@ get(); - $tasks = []; - - $left = count($businesses); - $now = Carbon::now()->toDateTimeString(); - $future = Carbon::now()->addDays(10)->toDateTimeString(); - foreach ($businesses as $business) { - foreach ($business->owners as $owner) { - foreach ($business->projects as $project) { - foreach ($business->members as $member) { - for ($i = 1; $i <= 30; $i++) { - foreach ($business->workflows as $workflow) { - $status = $workflow->workstatuses->random(); - $tasks[] = [ - 'business_id' => $business->id, -// 'creator_id' => $owner->id, -// 'project_id' => $project->id, -// 'user_id' => $member->id, -// 'workflow_id' => $workflow->id, -// 'work_status_id' => $status->id, -// 'name' => 'Task #' . rand(111111, 999999), -// 'time' => "10:00:00", -// 'cost' => 10000, -// 'completed' => $status->done, -// 'due_date' => $status->done ? $now : $future, - ]; - } - } - } + for ($i = 1; $i <= 1; $i++) { + \App\Task::insert( + factory(\App\Task::class, 2000)->raw() + ); + } + + $tags = [ + 1 => 'باربری', + 2 => 'زیر چاپ', + 3 => 'برون سپاری شود', + 4 => 'مشتری نپسندید', + ]; + + $ids = Task::select('id')->pluck('id')->toArray(); + $tag_task_relation = []; + foreach ($ids as $id) { + foreach (array_rand($tags, mt_rand(2, 4)) as $index => $tag) { + $tag_task_relation[] = [ + 'task_id' => $id, + 'tag_id' => $tag, + ]; + } + } + + TagTask::insert($tag_task_relation); + $this->createRelatedData(); + } + + public function createRelatedData() + { + Task::whereIn('business_id', [1, 2, 3, 4, 5]) + ->update(['business_id' => 1779, 'project_id' => 0, 'workflow_id' => 0, 'status_id' => 0, + 'system_id' => 0, 'sprint_id' => 0]); + + $count = Task::where('business_id', '=', 1779)->count(); + + $projects = [ + 3566 => [ + 'systems' => [8967, 8968, 8969, 8970], + 'sprints' => [], + ], + 3567 => [ + 'systems' => [8971], + 'sprints' => [526] + ], + 3568 => [ + 'systems' => [8972, 8973], + 'sprints' => [5480] + ] + ]; + + $workflows = [ + 2277 => [5100, 5101], + 2278 => [5102, 5103], + 2279 => [5104, 5105, 5106, 5107] + ]; + $take = $count > 50 ? 20 : 10; + $this->updateField(array_keys($projects), $count, $take, 'project_id'); + $this->updateField(array_keys($workflows), $count, $take, 'workflow_id'); + $this->fillSprintAndSystem($projects); + $this->fillStatues($workflows); + $this->fillTaskTag($count); + + } + + public function fillSprintAndSystem($projects) + { + foreach ($projects as $key => $value) { + $count = Task::where('project_id', $key)->count(); + $take = $count > 10 ? 2 : 1; + + $sprintIndex = 0; + $systemIndex = 0; + $i = 0; + while ($count > $i) { + Task::where('project_id', '=', $key) + ->where('system_id', 0)->where('sprint_id', 0) + ->take($take) + ->update( + [ + 'sprint_id' => isset($value['sprints'][$sprintIndex]) ? $value['sprints'][$sprintIndex] : 0, + 'system_id' => isset($value['systems'][$systemIndex]) ? $value['systems'][$systemIndex] : 0, + ]); + + $systemIndex += 1; + $sprintIndex += 1; + if ($systemIndex >= count($value['systems'])) { + $systemIndex = 0; } + if ($sprintIndex >= count($value['sprints'])) { + $sprintIndex = 0; + } + + + $i += $take; } + } + Task::whereIn('project_id', array_keys($projects)) + ->where('system_id', 0) + ->update(['system_id' => null]); - $this->command->info(--$left . " Business is left."); + Task::whereIn('project_id', array_keys($projects)) + ->where('sprint_id', 0) + ->update(['sprint_id' => null]); + } + + public function updateField($values, $count, $take, $field) + { + $i = 0; + $index = 0; + while ($count > $i) { + Task::where($field, '=', 0)->take($take)->update([$field => $values[$index]]); + $index += 1; + if (count($values) <= $index) { + $index = 0; + } + $i += $take; } + } + + public function fillStatues($workflows) + { + foreach ($workflows as $key => $value) { + $count = Task::where('workflow_id', $key)->count(); + $take = $count > 10 ? 2 : 1; + + $index = 0; + $i = 0; + while ($count > $i) { + Task::where('workflow_id', '=', $key) + ->where('status_id', 0) + ->take($take) + ->update( + [ + 'status_id' => $value[$index] + ]); - dd('here'); - $chunks = array_chunk($tasks, 1000); - $number = count($chunks); - foreach ($chunks as $value) { - DB::table('tasks')->insert($value); - $this->command->info(--$number . " Chunk is left."); + $index += 1; + if ($index >= count($value)) { + $index = 0; + } + + + $i += $take; + } } } + + public function fillTaskTag($count) + { + $tags = [5037, 5038, 5626]; + $tasks = Task::where('business_id', 1779)->get(); + foreach ($tasks as $task) { + $rand = rand(0,2); + $t = []; + do{ + array_push($t, new TagTask(['task_id' => $task->id, 'tag_id' => $tags[$rand]])); + $rand--; + }while($rand >= 0); + $task->tags()->saveMany($t); + } + + } } diff --git a/database/seeds/WorkSeeder.php b/database/seeds/WorkSeeder.php new file mode 100755 index 0000000..d4071de --- /dev/null +++ b/database/seeds/WorkSeeder.php @@ -0,0 +1,36 @@ +raw() + ); + + $businessId = 1779; + Work::inRandomOrder()->take(150)->update(['business_id' => $businessId]); + + $projects = [3566, 3567, 3568]; + + foreach ($projects as $projectId) { + Work::where('business_id', $businessId) + ->whereNotIn('project_id', $projects) + ->take(50)->update(['project_id' => $projectId, 'started_at' => \Carbon\Carbon::now()]); + } + + $works = Work::where('business_id', $businessId)->get(); + foreach ($works as $work){ + \DB::table('works')->where('id', $work->id) + ->update(['started_at' => \Carbon\Carbon::now()->subDays(rand(0,90))]); + } + } +} diff --git a/routes/api.php b/routes/api.php index 618384b..c92d4b3 100644 --- a/routes/api.php +++ b/routes/api.php @@ -163,3 +163,45 @@ $router->group(['prefix' => 'users'], function () use ($router) { $router->delete('/avatar', 'UserController@unSetAvatar'); }); }); +$router->group([], function () use ($router) { + $router->post('/log', 'ActivityController@store'); + $router->group(['prefix' => 'businesses/{business}', 'middleware' => ['auth', 'bindBusiness']], function (Router $router) { + $router->get('/tasks', 'TaskController@index'); + $router->get('/works', 'WorkController@index'); + $router->get('statistics', 'StatisticController@index'); + $router->get('projects/{project}/statistics', 'StatisticController@index'); + $router->get('/tasks/{task}', 'TaskController@show'); + $router->group(['prefix' => 'projects/{project}/tasks'], function (Router $router) { + $router->post('/', 'TaskController@store'); + + $router->group(['prefix' => '{task}'], function (Router $router) { + $router->get('/', 'TaskController@show'); + $router->put('/', 'TaskController@update'); + $router->delete('/', 'TaskController@destroy'); + + $router->post('/watchers', 'TaskController@toggleWatcher'); + + $router->group(['prefix' => 'works'], function (Router $router) { +// $router->get('/', 'WorkController@index'); + $router->post('/', 'WorkController@store'); + $router->group(['prefix' => '{work}'], function (Router $router) { + $router->get('/', 'WorkController@show'); + $router->put('/', 'WorkController@update'); + $router->delete('/', 'WorkController@destroy'); + }); + }); + + $router->group(['prefix' => 'comments'], function (Router $router) { + $router->get('/', 'CommentController@index'); + $router->post('/', 'CommentController@store'); + $router->group(['prefix' => '{comment}'], function (Router $router) { + $router->get('/', 'CommentController@show'); + $router->put('/', 'CommentController@update'); + $router->delete('/', 'CommentController@destroy'); + }); + }); + + }); + }); + }); +}); From 994c7d9b33938b9275e7dc40312033e76c590b70 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Tue, 2 Mar 2021 15:27:09 +0330 Subject: [PATCH 08/51] Merge docker services with main docker compose file --- app/Enums/business.php | 1 + app/Enums/comment.php | 1 + app/Enums/levels.php | 1 + app/Enums/log.php | 1 + app/Enums/post.php | 1 + app/Enums/roles.php | 1 + app/Enums/service.php | 1 + app/Enums/status.php | 1 + app/Enums/tables.php | 56 ++++ app/Enums/ticket.php | 1 + app/Enums/user.php | 1 + app/Utilities/Exceptions/Handler.php | 58 ++++ app/Utilities/Helpers/enum.php | 1 + app/Utilities/Helpers/http.php | 1 + app/Utilities/Helpers/index.php | 1 + app/Utilities/Helpers/permission.php | 244 +++++++++++++++++ app/Utilities/Jobs/AsyncCall.php | 1 + app/Utilities/Logger/CreateCustomLogger.php | 83 ++++++ .../Logger/LogServiceRecordProcessor.php | 37 +++ .../Middlewares/BindBusinessInfo.php | 1 + app/Utilities/Models/Model.php | 254 ++++++++++++++++++ app/Utilities/Models/ReportableRelation.php | 138 ++++++++++ .../Providers/AuthServiceProvider.php | 1 + .../Providers/HiLibraryServiceProvider.php | 49 ++++ config/amqp.php | 2 +- config/logging.php | 2 +- docker-compose.yml | 35 +++ 27 files changed, 972 insertions(+), 2 deletions(-) create mode 100644 app/Enums/business.php create mode 100644 app/Enums/comment.php create mode 100644 app/Enums/levels.php create mode 100644 app/Enums/log.php create mode 100644 app/Enums/post.php create mode 100644 app/Enums/roles.php create mode 100644 app/Enums/service.php create mode 100644 app/Enums/status.php create mode 100644 app/Enums/tables.php create mode 100644 app/Enums/ticket.php create mode 100644 app/Enums/user.php create mode 100644 app/Utilities/Exceptions/Handler.php create mode 100644 app/Utilities/Helpers/enum.php create mode 100644 app/Utilities/Helpers/http.php create mode 100644 app/Utilities/Helpers/index.php create mode 100644 app/Utilities/Helpers/permission.php create mode 100644 app/Utilities/Jobs/AsyncCall.php create mode 100644 app/Utilities/Logger/CreateCustomLogger.php create mode 100644 app/Utilities/Logger/LogServiceRecordProcessor.php create mode 100644 app/Utilities/Middlewares/BindBusinessInfo.php create mode 100644 app/Utilities/Models/Model.php create mode 100644 app/Utilities/Models/ReportableRelation.php create mode 100644 app/Utilities/Providers/AuthServiceProvider.php create mode 100644 app/Utilities/Providers/HiLibraryServiceProvider.php diff --git a/app/Enums/business.php b/app/Enums/business.php new file mode 100644 index 0000000..5690874 --- /dev/null +++ b/app/Enums/business.php @@ -0,0 +1 @@ + [ 'user' => 100, 'file' => 200, ] ]; \ No newline at end of file diff --git a/app/Enums/comment.php b/app/Enums/comment.php new file mode 100644 index 0000000..e516e01 --- /dev/null +++ b/app/Enums/comment.php @@ -0,0 +1 @@ + [ 'reject' => [ 'id' => 10, 'label' => 'رد' ], 'approve' => [ 'id' => 20, 'label' => 'تایید' ], ] ]; \ No newline at end of file diff --git a/app/Enums/levels.php b/app/Enums/levels.php new file mode 100644 index 0000000..8d1b627 --- /dev/null +++ b/app/Enums/levels.php @@ -0,0 +1 @@ + [ 'id' => 4, 'name' => 'Owner', 'label' => 'صاحب' ], 'admin' => [ 'id' => 3, 'name' => 'Admin', 'label' => 'مدیر' ], 'colleague' => [ 'id' => 2, 'name' => 'Colleague', 'label' => 'همکار' ], 'guest' => [ 'id' => 1, 'name' => 'Guest', 'label' => 'مهمان' ], 'inactive' => [ 'id' => 0, 'name' => 'Inactive', 'label' => 'غیر فعال' ], ]; \ No newline at end of file diff --git a/app/Enums/log.php b/app/Enums/log.php new file mode 100644 index 0000000..a49f08d --- /dev/null +++ b/app/Enums/log.php @@ -0,0 +1 @@ + [ User::class => 10, Business::class => 20, Project::class => 30, Task::class => 40, Spenthour::class => 50, ], 'actions' => [ 'created' => 10, 'updated' => 20, 'deleted' => 30, 'restored' => 40, ], ]; \ No newline at end of file diff --git a/app/Enums/post.php b/app/Enums/post.php new file mode 100644 index 0000000..0e8190c --- /dev/null +++ b/app/Enums/post.php @@ -0,0 +1 @@ + [ 'draft' => [ 'id' => 10, 'label' => 'پیش نویس' ], 'review' => [ 'id' => 20, 'label' => 'در حال بررسی' ], 'published' => [ 'id' => 30, 'label' => 'منتشر' ], 'trashed' => [ 'id' => 40, 'label' => 'حذف' ], ] ]; \ No newline at end of file diff --git a/app/Enums/roles.php b/app/Enums/roles.php new file mode 100644 index 0000000..6dd5eda --- /dev/null +++ b/app/Enums/roles.php @@ -0,0 +1 @@ + [ 'id' => 0, 'label' => 'غیر فعال' ], 'guest' => [ 'id' => 1, 'label' => 'میهمان' ], 'Colleague' => [ 'id' => 2, 'label' => 'همکار' ], 'senior' => [ 'id' => 3, 'label' => 'معاون' ], 'manager' => [ 'id' => 4, 'label' => 'مدیر' ], ]; \ No newline at end of file diff --git a/app/Enums/service.php b/app/Enums/service.php new file mode 100644 index 0000000..39ab78f --- /dev/null +++ b/app/Enums/service.php @@ -0,0 +1 @@ + [ 'file' => [ 'orphanage' => [ 'id' => 'orphanage', 'label' => 'داده‌های موقت', ] ] ] ]; \ No newline at end of file diff --git a/app/Enums/status.php b/app/Enums/status.php new file mode 100644 index 0000000..8bd99c4 --- /dev/null +++ b/app/Enums/status.php @@ -0,0 +1 @@ + [ 'inactive' => [ 'id' => 0, 'label' => 'غیر فعال', 'name' => 'Inactive' ], 'active' => [ 'id' => 1, 'label' => 'فعال', 'name' => 'Active' ], 'close' => [ 'id' => 2, 'label' => 'بسته', 'name' => 'Close' ], 'done' => [ 'id' => 3, 'label' => 'انجام شده', 'name' => 'Done' ], ], ]; \ No newline at end of file diff --git a/app/Enums/tables.php b/app/Enums/tables.php new file mode 100644 index 0000000..95a3d03 --- /dev/null +++ b/app/Enums/tables.php @@ -0,0 +1,56 @@ + [ + 'id' => 10, + 'name' => 'Businesses' + ], + 'projects' => [ + 'id' => 20, + 'name' => 'Projects' + ], + 'sprints' => [ + 'id' => 30, + 'name' => 'Sprints' + ], + 'statuses' => [ + 'id' => 40, + 'name' => 'Statuses' + ], + 'systems' => [ + 'id' => 50, + 'name' => 'Systems' + ], + 'workflows' => [ + 'id' => 60, + 'name' => 'Workflows' + ], + 'tags' => [ + 'id' => 70, + 'name' => 'Tags' + ], + 'tasks' => [ + 'id' => 80, + 'name' => 'Tasks' + ], + 'works' => [ + 'id' => 90, + 'name' => 'Works' + ], + + //Relation Table's + 'business_user' => [ + 'id' => 210, + 'name' => 'BusinessUser' + ], + 'project_user' => [ + 'id' => 220, + 'name' => 'ProjectUser' + ], + 'tag_task' => [ + 'id' => 230, + 'name' => 'TagTask' + ], +]; diff --git a/app/Enums/ticket.php b/app/Enums/ticket.php new file mode 100644 index 0000000..5899fbf --- /dev/null +++ b/app/Enums/ticket.php @@ -0,0 +1 @@ + [ 'sale' => [ 'id' => 1, 'label' => 'فروش', ], 'support' => [ 'id' => 2, 'label' => 'پشتیبانی' ] ], 'category' => [ 'webdesign-sale' => [ 'id' => 1, 'label' => 'طراحی سایت', 'type' => 1 ], 'seo-sale' => [ 'id' => 2, 'label' => 'فروش سئو', 'type' => 1 ], 'webdesign-support' => [ 'id' => 3, 'label' => 'پشتیبانی طراحی سایت', 'type' => 2 ] ], 'status' => [ 'active' => [ 'id' => 1, 'label' => 'فعال' ], 'close' => [ 'id' => 2, 'label' => 'بسته' ] ] ]; \ No newline at end of file diff --git a/app/Enums/user.php b/app/Enums/user.php new file mode 100644 index 0000000..e639578 --- /dev/null +++ b/app/Enums/user.php @@ -0,0 +1 @@ + [ 'guest' => [ 'id' => 1, 'label' => 'مهمان' ], 'user' => [ 'id' => 2, 'label' => 'کاربر' ], 'service' => [ 'id' => 3, 'label' => 'سرویس' ] ], 'status' => [ 'desable' => [ 'id' => 0, 'label' => 'غیر فعال' ], 'active' => [ 'id' => 1, 'label' => 'فعال' ] ], 'permissions' => [ // user 'user-user-create', 'user-user-view-any', 'user-user-update-own', 'user-user-update-any', 'user-user-role-own', 'user-user-role-any', 'user-role-create', 'user-role-view-any', // ticket 'ticket-ticket-create', 'ticket-ticket-view-any', 'ticket-ticket-reply-any', // post 'post-post-create', 'post-post-view-publish', 'post-post-view-any', 'post-post-view-own', 'post-post-update-own', 'post-post-update-any', 'post-post-delete-own', 'post-post-delete-any', // tag 'post-tag-update-any', 'post-tag-create', 'post-tag-delete-any', // taxonomies 'post-taxonomy-view-any', 'post-taxonomy-update-any', 'post-taxonomy-create', 'post-taxonomy-delete-any', // comments 'post-comment-view-any', 'post-comment-view-published', 'post-comment-view-own', 'post-comment-create', 'post-comment-update-any', 'post-comment-delete-any', ] ]; \ No newline at end of file diff --git a/app/Utilities/Exceptions/Handler.php b/app/Utilities/Exceptions/Handler.php new file mode 100644 index 0000000..79c1dec --- /dev/null +++ b/app/Utilities/Exceptions/Handler.php @@ -0,0 +1,58 @@ +getMethods( + ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED + ); + + + $result = []; + foreach ($methods as $method) { + // invoke the method so we can collect the result of execution it + $result[$method->name] = $method->invoke($exception); + } + + // Clear the unnecessary method + unset($result['getTrace']); + unset($result['__toString']); + + // clear the null values then encode it as json + // so we can decode it as an object in the Monolog Processor + $result = json_encode(array_filter($result)); + + return Log::emergency($result); + } + + public function render($request, Throwable $exception) + { + return parent::render($request, $exception); + } +} diff --git a/app/Utilities/Helpers/enum.php b/app/Utilities/Helpers/enum.php new file mode 100644 index 0000000..84fcfe8 --- /dev/null +++ b/app/Utilities/Helpers/enum.php @@ -0,0 +1 @@ +contains(".") ? "" : "."; // the first parameter of all enum keys are its filename [$filename, $key] = explode(".", $key, 2); // because we do not want to load the file every time use require $enums = require __DIR__ . "/../Enums/$filename.php"; // if the key that user provided not exists then null return $enums = Arr::get($enums, $key, null); // if enum null means that key not found throw_if($enums === null, 'Exception', "Undefined enum '{$key}'"); // if enum value is array its mean that user want to use it as collection return is_array($enums) ? collect($enums) : $enums; } } \ No newline at end of file diff --git a/app/Utilities/Helpers/http.php b/app/Utilities/Helpers/http.php new file mode 100644 index 0000000..bba7252 --- /dev/null +++ b/app/Utilities/Helpers/http.php @@ -0,0 +1 @@ +attach($piece->getFilename(), $piece); } } // if queue set then queue this command if ($queue !== null) { return dispatch(new AsyncCall(...func_get_args())); } try { // otherwise execute the pending request return $pendingRequest ->withToken($token) ->withoutRedirecting() ->withoutVerifying() ->$method($path, $data); } catch (Throwable $thr) { return $thr->response; } } } \ No newline at end of file diff --git a/app/Utilities/Helpers/index.php b/app/Utilities/Helpers/index.php new file mode 100644 index 0000000..64aade6 --- /dev/null +++ b/app/Utilities/Helpers/index.php @@ -0,0 +1 @@ +trim("."); if ($filename->isEmpty()) { continue; } require_once (string) $filename; } \ No newline at end of file diff --git a/app/Utilities/Helpers/permission.php b/app/Utilities/Helpers/permission.php new file mode 100644 index 0000000..a0e47ef --- /dev/null +++ b/app/Utilities/Helpers/permission.php @@ -0,0 +1,244 @@ +user()->id]['owner'] == true; + } +} + +/** + * Business Permit + */ + +if (!function_exists('businessAccess')) { + function businessAccess($ids) + { + return isset(request('_business_info')['info']['users'][$ids['user_id']?? auth()->id()]); + } +} + +if (!function_exists('isBusinessOwner')) { + function isBusinessOwner($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id'); + } +} + +if (!function_exists('isAtLeastBusinessAdmin')) { + function isAtLeastBusinessAdmin($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id'); + } +} + +if (!function_exists('businessEdit')) { + function businessEdit($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessUsers')) { + function businessUsers($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessWorkFlows')) { + function businessWorkFlows($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +if (!function_exists('businessProjects')) { + function businessProjects($ids) + { + return isBusinessOwner($ids); + } +} + +if (!function_exists('businessTags')) { + function businessTags($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +if (!function_exists('businessStatuses')) { + function businessStatuses($ids) + { + return isAtLeastBusinessAdmin($ids); + } +} + +/** + * Project Permit + */ + +if (!function_exists('isInProject')) { + function isInProject($ids) + { + return isset(request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]); + } +} + +if (!function_exists('isActiveInProject')) { + function isActiveInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] > 0; + } +} + +if (!function_exists('projectAccess')) { + function projectAccess($ids) + { + return isInProject($ids) && isActiveInProject($ids); + } +} + +if (!function_exists('isOwnerLevelInProject')) { + function isOwnerLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.owner.id'); + } +} + +if (!function_exists('isAtLeastAdminLevelInProject')) { + function isAtLeastAdminLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.admin.id'); + } +} + +if (!function_exists('isAtLeastColleagueLevelInProject')) { + function isAtLeastColleagueLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.colleague.id'); + } +} + +if (!function_exists('isAtLeastGuestLevelInProject')) { + function isAtLeastGuestLevelInProject($ids) + { + return request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] >= enum('levels.guest.id'); + } +} + +if (!function_exists('isDefiniteGuestInProject')) { + function isDefiniteGuestInProject($ids) + { + return isInProject($ids) && request('_business_info')['info']['projects'][$ids['project_id']]['members'][$ids['user_id'] ?? auth()->id()]['level'] == enum('levels.guest.id'); + } +} + +if (!function_exists('isProjectOwner')) { + function isProjectOwner($ids) + { + return isInProject($ids) && isOwnerLevelInProject($ids); + } +} + +if (!function_exists('isProjectAdmin')) { + function isProjectAdmin($ids) + { + return isInProject($ids) && isAtLeastAdminLevelInProject($ids); + } +} + +if (!function_exists('isProjectColleague')) { + function isProjectColleague($ids) + { + return isInProject($ids) && isAtLeastColleagueLevelInProject($ids); + } +} + +if (!function_exists('isProjectGuest')) { + function isProjectGuest($ids) + { + return isInProject($ids) && isAtLeastGuestLevelInProject($ids); + } +} + +if (!function_exists('projectEdit')) { + function projectEdit($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectUsers')) { + function projectUsers($ids) + { + return isProjectOwner($ids); + } +} + +if (!function_exists('projectSystems')) { + function projectSystems($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectTasks')) { + function projectTasks($ids) + { + return isProjectAdmin($ids); + } +} + +if (!function_exists('projectSprints')) { + function projectSprints($ids) + { + return isProjectAdmin($ids); + } +} + +/** + * other + */ + +if (!function_exists('isActiveUser')) { + function isActiveUser($ids) + { + return businessAccess($ids) && request('_business_info')['info']['users'][$ids['user_id'] ?? auth()->id()]['level'] > enum('levels.inactive.id'); + } +} + diff --git a/app/Utilities/Jobs/AsyncCall.php b/app/Utilities/Jobs/AsyncCall.php new file mode 100644 index 0000000..9f03b1d --- /dev/null +++ b/app/Utilities/Jobs/AsyncCall.php @@ -0,0 +1 @@ +method = $method; $this->service = $service; $this->path = $path; $this->data = $data; $this->onQueue($queue); $this->onConnection('database'); } /** * Execute the job. * * @return void */ public function handle() { call($this->method, $this->service, $this->path, $this->data); } } \ No newline at end of file diff --git a/app/Utilities/Logger/CreateCustomLogger.php b/app/Utilities/Logger/CreateCustomLogger.php new file mode 100644 index 0000000..53898b6 --- /dev/null +++ b/app/Utilities/Logger/CreateCustomLogger.php @@ -0,0 +1,83 @@ + Monolog::DEBUG, + 'info' => Monolog::INFO, + 'notice' => Monolog::NOTICE, + 'warning' => Monolog::WARNING, + 'error' => Monolog::ERROR, + 'critical' => Monolog::CRITICAL, + 'alert' => Monolog::ALERT, + 'emergency' => Monolog::EMERGENCY, + ]; + + public function __invoke(array $config) + { + if (!is_a($config['handler'], HandlerInterface::class, true)) { + throw new InvalidArgumentException( + $config['handler'] . ' must be an instance of ' . HandlerInterface::class + ); + } + + $with = array_merge( + ['level' => $this->level($config)], + $config['with'] ?? [], + $config['handler_with'] ?? [] + ); + + $handlers = [ + $this->prepareHandler(app()->make($config['handler'], $with), $config) + ]; + + $processors = [ + new LogServiceRecordProcessor, + ]; + + return new Monolog('custom', $handlers, $processors, new DateTimeZone('Asia/Tehran')); + } + + protected function getFallbackChannelName() + { + return app()->bound('env') ? app()->environment() : 'production'; + } + + protected function prepareHandler(HandlerInterface $handler, array $config = []) + { + $isHandlerFormattable = false; + + if (Monolog::API === 1) { + $isHandlerFormattable = true; + } elseif (Monolog::API === 2 && $handler instanceof FormattableHandlerInterface) { + $isHandlerFormattable = true; + } + + if ($isHandlerFormattable && !isset($config['formatter'])) { + $handler->setFormatter($this->formatter()); + } elseif ($isHandlerFormattable && $config['formatter'] !== 'default') { + $handler->setFormatter(app()->make($config['formatter'], $config['formatter_with'] ?? [])); + } + + return $handler; + } + + protected function level(array $config) + { + $level = $config['level'] ?? 'debug'; + + if (isset($this->levels[$level])) { + return $this->levels[$level]; + } + + throw new InvalidArgumentException('Invalid log level.'); + } +} diff --git a/app/Utilities/Logger/LogServiceRecordProcessor.php b/app/Utilities/Logger/LogServiceRecordProcessor.php new file mode 100644 index 0000000..c354dfc --- /dev/null +++ b/app/Utilities/Logger/LogServiceRecordProcessor.php @@ -0,0 +1,37 @@ + Uuid::uuid4()->toString(), + 'breadcrumbs' => DB::getQueryLog(), + 'request' => app('request'), + 'message' => Arr::get($exception, 'getMessage', $record['message']), + 'from' => env('CONTAINER_NAME'), + 'trace' => Arr::get($exception, 'getTraceAsString'), + 'name' => Arr::get($exception, 'getName'), + 'code' => Arr::get($exception, 'getCode'), + 'file' => Arr::get($exception, 'getFile'), + 'line' => Arr::get($exception, 'getLine'), + 'user_id' => Auth::hasResolvedGuards() ? Auth::id() : null, + 'created_at' => Carbon::now(), + ]); + } catch (Throwable $exception) { + return $record; + } + } +} diff --git a/app/Utilities/Middlewares/BindBusinessInfo.php b/app/Utilities/Middlewares/BindBusinessInfo.php new file mode 100644 index 0000000..04fbb6f --- /dev/null +++ b/app/Utilities/Middlewares/BindBusinessInfo.php @@ -0,0 +1 @@ +has('business_id') ? $request->business_id : $request->route('business'); $businessInfo = env('CONTAINER_NAME') == 'hi-user-app' ? \App\Business::info($business_id) : get('user', env('USER_URL') . 'actions/businesses/' . $business_id . '/info', []); $request->merge(['_business_info' => $businessInfo]); return $next($request); } } \ No newline at end of file diff --git a/app/Utilities/Models/Model.php b/app/Utilities/Models/Model.php new file mode 100644 index 0000000..6c66291 --- /dev/null +++ b/app/Utilities/Models/Model.php @@ -0,0 +1,254 @@ +action = static::CREATED; + }); + + static::updated(function ($model) { + $model->action = static::UPDATED; + }); + + static::deleted(function ($model) { + $model->action = static::DELETED; + }); + } + + /** + * @return void + * @throw \Exception + */ + public function rules() + { + return []; + } + + /** + * + * + * @param array $attributes + * @return void + */ + public function validate(array $attributes = null) + { + $attributes = $attributes ?? $this->getAttributes(); + + /** @var Validator $validator */ + $validator = app('validator')->make($attributes, $this->rules()); + + if ($validator->fails()) { + throw new ValidationException( + $validator, + new JsonResponse($validator->errors()->getMessages(), Response::HTTP_UNPROCESSABLE_ENTITY) + ); + } + } + + /** + * @return void + */ + public function updateRelations() + { + } + + /** + * @param string|null $key + * @return void + */ + public function getValueOf(?string $key) + { + $values = []; + + if ($key && isset($values, $key)) { + return $values[$key]; + } + + return $values; + } + + protected function makeChanges() + { + if (empty($this->reportable)) { + return; + } + + $changes = new Collection($this->getDirty()); + + // fillable * or field + $changes = $changes->filter(function ($value, $key) { + foreach ($this->reportable as $i => $name) { + if ($key === $name) { + return true; + } + } + return false; + }); + + if (($changes->isEmpty() && $this->action == static::UPDATED)) { + return; + } + + return [ + 'original' => $this->getOriginal() + $this->getAttributes(), + 'diff' => $changes->toArray(), + ]; + +// return [ +// 'auth' => Auth::id(), +// 'timestamp' => $this->freshTimestamp(), +// 'business' => $this->getValueOf('business_id'), +// 'info' => \request('_business_info')['info'] ?? null, +// 'project' => $this->getValueOf('project_id'), +// 'data' => [ +// 'sprint_id' => $this->getValueOf('sprint_id'), +// 'system_id' => $this->getValueOf('system_id'), +// 'workflow_id' => $this->getValueOf('workflow_id'), +// 'status_id' => $this->getValueOf('status_id'), +// 'user_id' => $this->getValueOf('user_id'), +// 'table_name' => $this->getTable(), +// 'crud_id' => $this->action, +// 'original' => $this->getOriginal() + $this->getAttributes(), +// 'diff' => $changes->toArray(), +// ], +// 'from' => env('CONTAINER_NAME'), +// ]; + } + + protected function report($changes): void + { + if ($this->action == null){ + return; + } + $payload = [ + 'auth' => Auth::id(), + 'timestamp' => $this->freshTimestamp(), + 'business' => $this->getValueOf('business_id'), + 'info' => \request('_business_info') ?? null, + 'project' => $this->getValueOf('project_id'), + 'data' => [ + 'sprint_id' => $this->getValueOf('sprint_id'), + 'system_id' => $this->getValueOf('system_id'), + 'workflow_id' => $this->getValueOf('workflow_id'), + 'status_id' => $this->getValueOf('status_id'), + 'task_id' => $this->getValueOf('task_id'), + 'subject_id' => $this->getValueOf('subject_id'), + 'user_id' => $this->getValueOf('user_id'), + 'table_name' => $this->getTable(), + 'crud_id' => $this->action, + 'original' => $changes['original'] + $this->getOriginal(), + 'diff' => $changes['diff'], + ], + 'from' => env('CONTAINER_NAME'), + ]; + + $message = new PublishableMessage(json_encode($payload)); + + $routers = [ + "activity_exchange" => ["name" => "activity",], + "notif_exchange" => ["name" => "notif",], + "socket_exchange" => ["name" => "socket",], + ]; + + foreach ($routers as $exchange => $properties) { + $message->setProperties(["application_headers" => new AMQPTable($properties)]); + + $message->setExchange(new Exchange($exchange)); + + Amqp::publish($message, ""); + } + } + + /** + * @param array $options + * @return void + */ + public function save(array $options = []) + { + // The validation function is called first + $this->validate(); + + // Then, because the relationships are set as attributes in this model + // we pre-enter their names in filled_relation attribute and store + // them in a temporary variable with a loop. + foreach ($this->fillable_relations as $relation) { + $this->filled_relations[$relation] = $this[$relation]; + unset($this[$relation]); + } + + // all of its action inside one transaction + // so if any of them failed the whole + // process rollbacked + DB::transaction(function () use ($options) { + // report to the activity aggregator + $changes = $this->makeChanges(); + + // save the model with it's attributes + parent::save($options); + + // save the model with it's relationships + $this->updateRelations(); + + is_array($changes) ? $this->report($changes) : true; + }, 3); + } + + public function delete() + { + $changes = $this->makeChanges(); + parent::delete(); + is_array($changes) ? $this->report($changes) : true; + } +} diff --git a/app/Utilities/Models/ReportableRelation.php b/app/Utilities/Models/ReportableRelation.php new file mode 100644 index 0000000..52f96d3 --- /dev/null +++ b/app/Utilities/Models/ReportableRelation.php @@ -0,0 +1,138 @@ +action = static::CREATED; + static::report($model); + }); + static::updated(function ($model) { + $model->action = static::UPDATED; + static::report($model); + }); + static::deleted(function ($model) { + $model->action = static::DELETED; + static::report($model); + }); + } + + public static function report($model) + { + $payload = [ + 'auth' => Auth::id(), + 'timestamp' => $model->freshTimestamp(), + 'business' => $model->pivotParent->getValueOf('business_id'), + 'info' => \request('_business_info') ?? null, + 'project' => $model->pivotParent->getValueOf('project_id'), + 'data' => [ + 'sprint_id' => $model->pivotParent->getValueOf('sprint_id'), + 'system_id' => $model->pivotParent->getValueOf('system_id'), + 'workflow_id' => $model->pivotParent->getValueOf('workflow_id'), + 'status_id' => $model->pivotParent->getValueOf('status_id'), + 'task_id' => $model->pivotParent->getValueOf('task_id'), + 'user_id' => $model->user_id, + 'table_name' => $model->getTable(), + 'crud_id' => $model->action, + 'original' => $model->getOriginal() + $model->getAttributes(), + 'diff' => $model->getChanges(), + ], + 'from' => env('CONTAINER_NAME'), + ]; + + $message = new PublishableMessage(json_encode($payload)); + + $routers = [ + "activity_exchange" => ["name" => "activity",], + "notif_exchange" => ["name" => "notif",], + "socket_exchange" => ["name" => "socket",], + ]; + + foreach ($routers as $exchange => $properties) { + $message->setProperties(["application_headers" => new AMQPTable($properties)]); + + $message->setExchange(new Exchange($exchange)); + + Amqp::publish($message, ""); + } + } + + public function properties() + { + return $properties = [ + // Message properties + 'Persistent' => '1', // Marks a message as persistent + + // those familiar with the protocol may choose to use this property instead of Persistent. They control the same thing. + // Non-persistent (1) or persistent (2). + 'DeliveryMode' => '', + + // Used to describe the mime-type of the encoding. For example for the often used JSON encoding it is a good practice to set this property to: application/json. + // MIME content type. + // short string (max. 256 characters) + 'content_type' => '', + + // Commonly used to name a callback queue. + // Address to reply to. + // short string (max. 256 characters) + 'reply_to' => '', + + // Useful to correlate RPC responses with requests. + // Application correlation identifier. + // short string (max. 256 characters) + 'correlation_id' => '', + + /** Rarley Used Properties */ + + // This defines the message priority + // Message priority, 0 to 9 + 'priority' => '', + + // This is the message time stamp + // Message timestamp. + 'timestamp' => '', + + // This is the broker with whom the user sends the message (by default, it is "guest"). + // Creating user id. + // short string (max. 256 characters) + 'user_id' => '', + + // MIME content encoding. + // short string (max. 256 characters) + 'content_encoding' => '', + + // Message expiration specification. + // short string (max. 256 characters) + 'expiration' => '', + + // Application message identifier. + // short string (max. 256 characters) + 'message_id' => '', + + // Message type name. + // short string (max. 256 characters) + 'type' => '', + + // Creating application id. + // short string (max. 256 characters) + 'app_id' => '', + 'cluster_id' => '', + ]; + } +} diff --git a/app/Utilities/Providers/AuthServiceProvider.php b/app/Utilities/Providers/AuthServiceProvider.php new file mode 100644 index 0000000..d64ae2d --- /dev/null +++ b/app/Utilities/Providers/AuthServiceProvider.php @@ -0,0 +1 @@ +app['auth']->viaRequest('api', function (Request $request) { // with token if ($request->bearerToken()) { return $this->loginUserWithToken($request); } // first party if ($request->getHost() === $container_name = getenv('CONTAINER_NAME')) { return $this->loginServiceWithSetUser($container_name); } // without token return null; }); } public function loginUserWithToken(Request $request): User { $attributes = Http::retry(3, 100) ->withToken($request->bearerToken()) ->get(env('USER_URL') . 'auth') ->json(); $attributes = Arr::get($attributes, 'data',[]); // dd($attributes); return new User($attributes); } public function loginServiceWithSetUser(string $container_name): User { Auth::setUser($user = new User([ 'name' => $container_name, 'is_service' => true, ])); return $user; } } \ No newline at end of file diff --git a/app/Utilities/Providers/HiLibraryServiceProvider.php b/app/Utilities/Providers/HiLibraryServiceProvider.php new file mode 100644 index 0000000..6c8ac8b --- /dev/null +++ b/app/Utilities/Providers/HiLibraryServiceProvider.php @@ -0,0 +1,49 @@ +app->singleton( + \Illuminate\Contracts\Debug\ExceptionHandler::class, + \App\HiLib\Exceptions\Handler::class + ); + + Stringable::macro('then', function($callback) { + return new Stringable($callback($this)); + }); + } + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + DB::enableQueryLog(); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + // + } +} diff --git a/config/amqp.php b/config/amqp.php index d0734cf..1b1f582 100644 --- a/config/amqp.php +++ b/config/amqp.php @@ -11,7 +11,7 @@ return [ 'rabbitmq' => [ 'connection' => [ - 'host' => env('AMQP_HOST', 'base-rabbitmq'), + 'host' => env('AMQP_HOST', 'liwo_redis_1'), 'port' => env('AMQP_PORT', 5672), 'username' => env('AMQP_USERNAME', 'root'), 'password' => env('AMQP_PASSWORD', 'root'), diff --git a/config/logging.php b/config/logging.php index 31b0dcf..9013913 100644 --- a/config/logging.php +++ b/config/logging.php @@ -16,7 +16,7 @@ return [ 'handler' => AmqpHandler::class, 'with' => [ 'exchange' => new AMQPChannel( - new AMQPStreamConnection("base-rabbitmq", 5672, 'root', 'root') + new AMQPStreamConnection("liwo_redis_1", 5672, 'root', 'root') ), 'exchangeName' => 'log_exchange' ], diff --git a/docker-compose.yml b/docker-compose.yml index 53c8d2c..46e149f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,39 @@ # For more information: https://laravel.com/docs/sail version: '3' services: + minio: + image: minio/minio + command: server /data + ports: + - 9000:9000 + environment: + MINIO_ACCESS_KEY: root + MINIO_SECRET_KEY: minioroot + networks: + - sail + commander: + image: rediscommander/redis-commander:latest + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + - HTTP_USER=root + - HTTP_PASSWORD=root + - REDIS_PASSWORD=root + ports: + - 8081:8081 + depends_on: + - redis + networks: + - sail + redis: + image: redis:latest + ports: + - 6379:6379 + command: redis-server --requirepass root + volumes: + - redis-data:/data + networks: + - sail laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.0 @@ -51,5 +84,7 @@ networks: sail: driver: bridge volumes: + redis-data: + driver: local sailmysql: driver: local From 5866521e83163b1a5cc6789c3288a43f47b00774 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Tue, 2 Mar 2021 16:15:03 +0330 Subject: [PATCH 09/51] Yet another quality commit. --- app/Models/Activity.php | 6 +- app/Models/Business.php | 2 +- app/Models/Cost.php | 2 +- app/Models/File.php | 2 +- app/Models/Fingerprint.php | 2 +- app/Models/Project.php | 2 +- app/Models/SoftDeletes.php | 2 +- app/Models/Sprint.php | 2 +- app/Models/Status.php | 2 +- app/Models/System.php | 2 +- app/Models/Tag.php | 2 +- app/Models/TagTask.php | 4 +- app/Models/Transaction.php | 2 +- app/Models/User.php | 2 +- app/Models/Workflow.php | 2 +- app/Scopes/BusinessScope.php | 18 -- app/Statists/Pipe.php | 8 - app/Statists/SprintStatist.php | 44 --- app/Statists/SystemStatist.php | 22 -- app/Statists/TagStatist.php | 54 ---- app/Utilities/Exceptions/Handler.php | 2 +- app/Utilities/Jobs/AsyncCall.php | 42 ++- app/Utilities/Logger/CreateCustomLogger.php | 2 +- .../Logger/LogServiceRecordProcessor.php | 2 +- .../Middlewares/BindBusinessInfo.php | 23 +- app/Utilities/Models/Model.php | 2 +- app/Utilities/Models/ReportableRelation.php | 2 +- .../Providers/AuthServiceProvider.php | 55 +++- .../Providers/HiLibraryServiceProvider.php | 2 +- composer.json | 32 ++- composer.lock | 259 +++--------------- config.default/app.php | 232 ---------------- config.default/filesystems.php | 72 ----- config.default/logging.php | 104 ------- config.default/services.php | 33 --- config/amqp.php | 73 ----- config/app.php | 93 ++++++- {config.default => config}/auth.php | 0 {config.default => config}/broadcasting.php | 0 {config.default => config}/cache.php | 0 config/cors.php | 43 +-- config/cors.php.laravel | 59 ++++ .../cors.php => config/cors.php.lumen | 0 {config.default => config}/database.php | 0 {config.default => config}/hashing.php | 0 config/logging.php | 2 +- {config.default => config}/mail.php | 0 {config.default => config}/queue.php | 0 {config.default => config}/session.php | 0 {config.default => config}/view.php | 0 definitions.json | 152 ++++++++++ docker-compose.yml | 24 +- routes/api.php | 16 +- 53 files changed, 534 insertions(+), 974 deletions(-) delete mode 100644 app/Scopes/BusinessScope.php delete mode 100644 app/Statists/Pipe.php delete mode 100644 app/Statists/SprintStatist.php delete mode 100644 app/Statists/SystemStatist.php delete mode 100644 app/Statists/TagStatist.php delete mode 100644 config.default/app.php delete mode 100644 config.default/filesystems.php delete mode 100644 config.default/logging.php delete mode 100644 config.default/services.php delete mode 100644 config/amqp.php rename {config.default => config}/auth.php (100%) rename {config.default => config}/broadcasting.php (100%) rename {config.default => config}/cache.php (100%) create mode 100644 config/cors.php.laravel rename config.default/cors.php => config/cors.php.lumen (100%) rename {config.default => config}/database.php (100%) rename {config.default => config}/hashing.php (100%) rename {config.default => config}/mail.php (100%) rename {config.default => config}/queue.php (100%) rename {config.default => config}/session.php (100%) rename {config.default => config}/view.php (100%) create mode 100644 definitions.json diff --git a/app/Models/Activity.php b/app/Models/Activity.php index cf4c1d1..29e7ef2 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -1,10 +1,10 @@ where('business_id', '=', request('business_id')); - } -} diff --git a/app/Statists/Pipe.php b/app/Statists/Pipe.php deleted file mode 100644 index cf1ca1c..0000000 --- a/app/Statists/Pipe.php +++ /dev/null @@ -1,8 +0,0 @@ -task = $task; - } - - public function handle(&$result, $next) - { - $key = 'sprints.'; - $key .= ($this->task->sprint_id ?? 0).'.'; - $key .= ($this->task->assignee_id ?? 0).'.'; - $key .= $this->task->workflow_id . '.'; - $key .= $this->task->status_id; - - $map = [ - 'total' => 0, - 'test' => 0, - 'overdue' => 0, - 'worked' => 0, // total worked in minute - 'estimate' => 0, // estimate total - ]; - - $node = Arr::get($result, $key, $map); - - $node['total'] = $node['total'] + 1; - $node['test'] = $node['test'] + $result->ready_to_test; - $node['overdue'] = $node['overdue'] + ($result->on_time? 0 : 1); - - Arr::set($result, $key, $node); - - return $next($result); - } -} diff --git a/app/Statists/SystemStatist.php b/app/Statists/SystemStatist.php deleted file mode 100644 index c268aa3..0000000 --- a/app/Statists/SystemStatist.php +++ /dev/null @@ -1,22 +0,0 @@ -system_id ?? 0) . '.' . ($task->assignee_id ?? 0) . - '.' . $task->workflow_id . '.' . $task->status_id; - $node = Arr::get($result, $key, ['total' => 0, 'test' => 0, 'overdue' => 0]); - $node['total'] = $node['total'] + 1; - $node['test'] = $node['test'] + $task->ready_to_test; - $node['overdue'] = $node['overdue'] + ($task->on_time? 0: 1); - Arr::set($result, $key, $node); - - return $next($tasks); - } -} diff --git a/app/Statists/TagStatist.php b/app/Statists/TagStatist.php deleted file mode 100644 index 33bd15f..0000000 --- a/app/Statists/TagStatist.php +++ /dev/null @@ -1,54 +0,0 @@ -first()->project_id; - - $tags = $tasks->pluck('tag_id'); - $users = $tasks->pluck('assignee_id'); - $workflows = $tasks->pluck('workflow_id'); - $statuses = $tasks->pluck('status_id'); - - foreach ($tags as $tag) { - foreach ($users as $user) { - foreach ($workflows as $workflow) { - foreach ($statuses as $status) { - $filter = $tasks - ->where('sprint_id', '=', $tag) - ->where('assignee_id', '=', $user) - ->where('workflow_id', '=', $workflow) - ->where('status_id', '=', $status); - - if ($filter->isEmpty()) { - continue; - } - - $stat = [ - 'task_count' => $filter->count(), - 'total_worked_in_minute' => $filter->sum('spent_time'), - 'overdue_count' => $filter->where('on_time','=',false)->count(), - 'total_estimate_in_minute' => $filter->sum('estimated_time'), - 'test_flag_count' => $filter->where('ready_to_test', '=', true)->count(), - ]; - - Arr::set($result,"{$tag}.{$user}.{$workflow}.{$status}",$stat); - } - } - } - } - - - Cache::put($project, $result); - - return $next($tasks); - } -} diff --git a/app/Utilities/Exceptions/Handler.php b/app/Utilities/Exceptions/Handler.php index 79c1dec..8d01e2b 100644 --- a/app/Utilities/Exceptions/Handler.php +++ b/app/Utilities/Exceptions/Handler.php @@ -1,6 +1,6 @@ method = $method; $this->service = $service; $this->path = $path; $this->data = $data; $this->onQueue($queue); $this->onConnection('database'); } /** * Execute the job. * * @return void */ public function handle() { call($this->method, $this->service, $this->path, $this->data); } } \ No newline at end of file +method = $method; + $this->service = $service; + $this->path = $path; + $this->data = $data; + $this->onQueue($queue); + $this->onConnection('database'); + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + call($this->method, $this->service, $this->path, $this->data); + } +} diff --git a/app/Utilities/Logger/CreateCustomLogger.php b/app/Utilities/Logger/CreateCustomLogger.php index 53898b6..ab07317 100644 --- a/app/Utilities/Logger/CreateCustomLogger.php +++ b/app/Utilities/Logger/CreateCustomLogger.php @@ -1,6 +1,6 @@ has('business_id') ? $request->business_id : $request->route('business'); $businessInfo = env('CONTAINER_NAME') == 'hi-user-app' ? \App\Business::info($business_id) : get('user', env('USER_URL') . 'actions/businesses/' . $business_id . '/info', []); $request->merge(['_business_info' => $businessInfo]); return $next($request); } } \ No newline at end of file +has('business_id') ? $request->business_id : $request->route('business'); + $businessInfo = env('CONTAINER_NAME') == 'hi-user-app' ? + \App\Business::info($business_id) : + get('user', env('USER_URL') . 'actions/businesses/' . $business_id . '/info', []); + + + $request->merge(['_business_info' => $businessInfo]); + + return $next($request); + } +} diff --git a/app/Utilities/Models/Model.php b/app/Utilities/Models/Model.php index 6c66291..1dd9b0f 100644 --- a/app/Utilities/Models/Model.php +++ b/app/Utilities/Models/Model.php @@ -1,6 +1,6 @@ app['auth']->viaRequest('api', function (Request $request) { // with token if ($request->bearerToken()) { return $this->loginUserWithToken($request); } // first party if ($request->getHost() === $container_name = getenv('CONTAINER_NAME')) { return $this->loginServiceWithSetUser($container_name); } // without token return null; }); } public function loginUserWithToken(Request $request): User { $attributes = Http::retry(3, 100) ->withToken($request->bearerToken()) ->get(env('USER_URL') . 'auth') ->json(); $attributes = Arr::get($attributes, 'data',[]); // dd($attributes); return new User($attributes); } public function loginServiceWithSetUser(string $container_name): User { Auth::setUser($user = new User([ 'name' => $container_name, 'is_service' => true, ])); return $user; } } \ No newline at end of file +app['auth']->viaRequest('api', function (Request $request) { + // with token + if ($request->bearerToken()) { + return $this->loginUserWithToken($request); + } + + // first party + if ($request->getHost() === $container_name = getenv('CONTAINER_NAME')) { + return $this->loginServiceWithSetUser($container_name); + } + + // without token + return null; + }); + } + + public function loginUserWithToken(Request $request): User + { + $attributes = Http::retry(3, 100) + ->withToken($request->bearerToken()) + ->get(env('USER_URL') . 'auth') + ->json(); + + $attributes = Arr::get($attributes, 'data',[]); + +// dd($attributes); + return new User($attributes); + } + + public function loginServiceWithSetUser(string $container_name): User + { + Auth::setUser($user = new User([ + 'name' => $container_name, + 'is_service' => true, + ])); + + return $user; + } +} diff --git a/app/Utilities/Providers/HiLibraryServiceProvider.php b/app/Utilities/Providers/HiLibraryServiceProvider.php index 6c8ac8b..7ac7c8e 100644 --- a/app/Utilities/Providers/HiLibraryServiceProvider.php +++ b/app/Utilities/Providers/HiLibraryServiceProvider.php @@ -1,6 +1,6 @@ = 7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" - }, - "time": "2020-10-15T08:29:30+00:00" - }, { "name": "php-amqplib/php-amqplib", - "version": "v2.12.3", + "version": "v2.12.1", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "f746eb44df6d8f838173729867dd1d20b0265faa" + "reference": "0eaaa9d5d45335f4342f69603288883388c2fe21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/f746eb44df6d8f838173729867dd1d20b0265faa", - "reference": "f746eb44df6d8f838173729867dd1d20b0265faa", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/0eaaa9d5d45335f4342f69603288883388c2fe21", + "reference": "0eaaa9d5d45335f4342f69603288883388c2fe21", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-sockets": "*", - "php": ">=5.6.3,<8.0", - "phpseclib/phpseclib": "^2.0|^3.0" + "php": ">=5.6.3", + "phpseclib/phpseclib": "^2.0.0" }, "conflict": { "php": "7.4.0 - 7.4.1" @@ -3139,7 +3022,7 @@ "ext-curl": "*", "nategood/httpful": "^0.2.20", "phpunit/phpunit": "^5.7|^6.5|^7.0", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^2.5" }, "type": "library", "extra": { @@ -3186,9 +3069,9 @@ ], "support": { "issues": "https://github.com/php-amqplib/php-amqplib/issues", - "source": "https://github.com/php-amqplib/php-amqplib/tree/v2.12.3" + "source": "https://github.com/php-amqplib/php-amqplib/tree/v2.12.1" }, - "time": "2021-03-01T12:21:31+00:00" + "time": "2020-09-25T18:34:58+00:00" }, { "name": "phpoption/phpoption", @@ -3261,26 +3144,24 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.5", + "version": "2.0.30", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede" + "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7c751ea006577e4c2e83326d90c8b1e8c11b8ede", - "reference": "7c751ea006577e4c2e83326d90c8b1e8c11b8ede", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/136b9ca7eebef78be14abf90d65c5e57b6bc5d36", + "reference": "136b9ca7eebef78be14abf90d65c5e57b6bc5d36", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1|^2", - "paragonie/random_compat": "^1.4|^2.0|^9.99.99", - "php": ">=5.6.1" + "php": ">=5.3.3" }, "require-dev": { "phing/phing": "~2.7", - "phpunit/phpunit": "^5.7|^6.0|^9.4", + "phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4", "squizlabs/php_codesniffer": "~2.0" }, "suggest": { @@ -3295,7 +3176,7 @@ "phpseclib/bootstrap.php" ], "psr-4": { - "phpseclib3\\": "phpseclib/" + "phpseclib\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3352,7 +3233,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.5" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.30" }, "funding": [ { @@ -3368,7 +3249,7 @@ "type": "tidelift" } ], - "time": "2021-02-12T16:18:16+00:00" + "time": "2020-12-17T05:42:04+00:00" }, { "name": "psr/cache", @@ -4140,29 +4021,30 @@ }, { "name": "spatie/laravel-medialibrary", - "version": "8.10.1", + "version": "9.4.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "72b408972ba0b994eb52f39d38169621699e549b" + "reference": "fe8dcb2a56b3061cd29d072c1e983a1e035a1671" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/72b408972ba0b994eb52f39d38169621699e549b", - "reference": "72b408972ba0b994eb52f39d38169621699e549b", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/fe8dcb2a56b3061cd29d072c1e983a1e035a1671", + "reference": "fe8dcb2a56b3061cd29d072c1e983a1e035a1671", "shasum": "" }, "require": { + "ext-exif": "*", "ext-fileinfo": "*", "ext-json": "*", - "illuminate/bus": "^6.18|^7.0|^8.0", - "illuminate/console": "^6.18|^7.0|^8.0", - "illuminate/database": "^6.18|^7.0|^8.0", - "illuminate/pipeline": "^6.18|^7.0|^8.0", - "illuminate/support": "^6.18|^7.0|^8.0", + "illuminate/bus": "^7.0|^8.0", + "illuminate/console": "^7.0|^8.0", + "illuminate/database": "^7.0|^8.0", + "illuminate/pipeline": "^7.0|^8.0", + "illuminate/support": "^7.0|^8.0", "league/flysystem": "^1.0.64", "maennchen/zipstream-php": "^1.0|^2.0", - "php": "^7.4", + "php": "^7.4|^8.0", "spatie/image": "^1.4.0", "spatie/temporary-directory": "^1.1", "symfony/console": "^4.4|^5.0" @@ -4178,8 +4060,7 @@ "guzzlehttp/guzzle": "^6.3|^7.0", "league/flysystem-aws-s3-v3": "^1.0.23", "mockery/mockery": "^1.3", - "orchestra/testbench": "^4.0|^5.0|^6.0", - "php-ffmpeg/php-ffmpeg": "^0.16.0", + "orchestra/testbench": "^5.0|^6.0", "phpunit/phpunit": "^9.1", "spatie/pdf-to-image": "^2.0", "spatie/phpunit-snapshot-assertions": "^4.0" @@ -4228,7 +4109,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/8.10.1" + "source": "https://github.com/spatie/laravel-medialibrary/tree/9.4.2" }, "funding": [ { @@ -4240,7 +4121,7 @@ "type": "github" } ], - "time": "2020-10-05T21:54:57+00:00" + "time": "2021-01-15T07:55:54+00:00" }, { "name": "spatie/laravel-query-builder", @@ -4363,76 +4244,6 @@ }, "time": "2020-11-09T15:54:21+00:00" }, - { - "name": "spatie/laravel-query-builder", - "version": "3.3.4", - "source": { - "type": "git", - "url": "https://github.com/spatie/laravel-query-builder.git", - "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-query-builder/zipball/2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", - "reference": "2e131b0c8ae600b6e3aabb5a1501c721862a0b8f", - "shasum": "" - }, - "require": { - "illuminate/database": "^6.0|^7.0|^8.0", - "illuminate/http": "^6.0|^7.0|^8.0", - "illuminate/support": "^6.0|^7.0|^8.0", - "php": "^7.3|^8.0" - }, - "require-dev": { - "ext-json": "*", - "laravel/legacy-factories": "^1.0.4", - "mockery/mockery": "^1.4", - "orchestra/testbench": "^4.9|^5.8|^6.3", - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Spatie\\QueryBuilder\\QueryBuilderServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Spatie\\QueryBuilder\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alex Vanderbist", - "email": "alex@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Easily build Eloquent queries from API requests", - "homepage": "https://github.com/spatie/laravel-query-builder", - "keywords": [ - "laravel-query-builder", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/laravel-query-builder/issues", - "source": "https://github.com/spatie/laravel-query-builder" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - } - ], - "time": "2020-11-26T14:51:30+00:00" - }, { "name": "swiftmailer/swiftmailer", "version": "v6.2.5", @@ -9515,9 +9326,9 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.3|^8.0", "ext-gd": "*", - "ext-json": "*" + "ext-json": "*", + "php": "^7.3|^8.0" }, "platform-dev": [], "plugin-api-version": "2.0.0" diff --git a/config.default/app.php b/config.default/app.php deleted file mode 100644 index 2a2f0eb..0000000 --- a/config.default/app.php +++ /dev/null @@ -1,232 +0,0 @@ - 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, - - ], - -]; diff --git a/config.default/filesystems.php b/config.default/filesystems.php deleted file mode 100644 index 10c9d9b..0000000 --- a/config.default/filesystems.php +++ /dev/null @@ -1,72 +0,0 @@ - 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'), - ], - -]; diff --git a/config.default/logging.php b/config.default/logging.php deleted file mode 100644 index 6aa77fe..0000000 --- a/config.default/logging.php +++ /dev/null @@ -1,104 +0,0 @@ - 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'), - ], - ], - -]; diff --git a/config.default/services.php b/config.default/services.php deleted file mode 100644 index 2a1d616..0000000 --- a/config.default/services.php +++ /dev/null @@ -1,33 +0,0 @@ - [ - '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'), - ], - -]; diff --git a/config/amqp.php b/config/amqp.php deleted file mode 100644 index 1b1f582..0000000 --- a/config/amqp.php +++ /dev/null @@ -1,73 +0,0 @@ - env('AMQP_CONNECTION', 'rabbitmq'), - - /*Available connections*/ - 'connections' => [ - - 'rabbitmq' => [ - 'connection' => [ - 'host' => env('AMQP_HOST', 'liwo_redis_1'), - '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), - ], - ], - ], -]; diff --git a/config/app.php b/config/app.php index 1675900..cc438b2 100644 --- a/config/app.php +++ b/config/app.php @@ -1,13 +1,18 @@ env('APP_NAME', 'Laravel'), 'env' => env('APP_ENV', 'production'), 'debug' => (bool) env('APP_DEBUG', false), - 'timezone' => 'Asia/Tehran', + 'url' => env('APP_URL', 'http://localhost'), + + 'asset_url' => env('ASSET_URL', null), + + 'timezone' => 'UTC', 'locale' => 'fa', @@ -15,5 +20,89 @@ return [ 'faker_locale' => 'fa_IR', - 'cache_ttl' => 60 + 'key' => env('APP_KEY'), + + 'cipher' => 'AES-256-CBC', + + '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, + ], + + '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, + ], ]; diff --git a/config.default/auth.php b/config/auth.php similarity index 100% rename from config.default/auth.php rename to config/auth.php diff --git a/config.default/broadcasting.php b/config/broadcasting.php similarity index 100% rename from config.default/broadcasting.php rename to config/broadcasting.php diff --git a/config.default/cache.php b/config/cache.php similarity index 100% rename from config.default/cache.php rename to config/cache.php diff --git a/config/cors.php b/config/cors.php index c3ded3c..8a39e6d 100644 --- a/config/cors.php +++ b/config/cors.php @@ -1,59 +1,34 @@ ['/*'], + 'paths' => ['api/*', 'sanctum/csrf-cookie'], - /* - * 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' => ['*'], - /* - * Patterns that can be used with `preg_match` to match the origin. - */ - 'allowedOriginsPatterns' => ['Content-Type', 'X-Requested-With'], + 'allowed_origins_patterns' => [], - /* - * 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, + ]; diff --git a/config/cors.php.laravel b/config/cors.php.laravel new file mode 100644 index 0000000..c3ded3c --- /dev/null +++ b/config/cors.php.laravel @@ -0,0 +1,59 @@ + ['/*'], + + /* + * 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' => ['*'], + + /* + * 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, +]; diff --git a/config.default/cors.php b/config/cors.php.lumen similarity index 100% rename from config.default/cors.php rename to config/cors.php.lumen diff --git a/config.default/database.php b/config/database.php similarity index 100% rename from config.default/database.php rename to config/database.php diff --git a/config.default/hashing.php b/config/hashing.php similarity index 100% rename from config.default/hashing.php rename to config/hashing.php diff --git a/config/logging.php b/config/logging.php index 9013913..c6f3fe5 100644 --- a/config/logging.php +++ b/config/logging.php @@ -16,7 +16,7 @@ return [ 'handler' => AmqpHandler::class, 'with' => [ 'exchange' => new AMQPChannel( - new AMQPStreamConnection("liwo_redis_1", 5672, 'root', 'root') + new AMQPStreamConnection("liwo_rabbitmq_1", 5672, 'root', 'root') ), 'exchangeName' => 'log_exchange' ], diff --git a/config.default/mail.php b/config/mail.php similarity index 100% rename from config.default/mail.php rename to config/mail.php diff --git a/config.default/queue.php b/config/queue.php similarity index 100% rename from config.default/queue.php rename to config/queue.php diff --git a/config.default/session.php b/config/session.php similarity index 100% rename from config.default/session.php rename to config/session.php diff --git a/config.default/view.php b/config/view.php similarity index 100% rename from config.default/view.php rename to config/view.php diff --git a/definitions.json b/definitions.json new file mode 100644 index 0000000..10ab903 --- /dev/null +++ b/definitions.json @@ -0,0 +1,152 @@ +{ + "queues": [ + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "activity_queue", + "type": "classic", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "notif_queue", + "type": "classic", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "socket_queue", + "type": "classic", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "log_queue", + "type": "classic", + "vhost": "/" + } + ], + "exchanges": [ + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "activity_exchange", + "type": "headers", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "notif_exchange", + "type": "headers", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "socket_exchange", + "type": "headers", + "vhost": "/" + }, + { + "arguments": {}, + "auto_delete": false, + "durable": true, + "name": "log_exchange", + "type": "fanout", + "vhost": "/" + } + ], + "bindings": [ + { + "arguments": { + "x-match": "any", + "name" : "activity" + }, + "destination": "activity_queue", + "destination_type": "queue", + "routing_key": "", + "source": "activity_exchange", + "vhost": "/" + }, + { + "arguments": { + "x-match": "any", + "name" : "notif" + }, + "destination": "notif_queue", + "destination_type": "queue", + "routing_key": "", + "source": "notif_exchange", + "vhost": "/" + }, + { + "arguments": { + "x-match": "any", + "name" : "socket" + }, + "destination": "socket_queue", + "destination_type": "queue", + "routing_key": "", + "source": "socket_exchange", + "vhost": "/" + }, + { + "arguments": {}, + "destination": "log_queue", + "destination_type": "queue", + "routing_key": "", + "source": "log_exchange", + "vhost": "/" + } + ], + + "global_parameters": [ + { + "name": "cluster_name", + "value": "rabbit@258a143f3102" + } + ], + "parameters": [], + "permissions": [ + { + "configure": ".*", + "read": ".*", + "user": "root", + "vhost": "/", + "write": ".*" + } + ], + "policies": [], + "rabbit_version": "3.8.5", + "rabbitmq_version": "3.8.5", + "topic_permissions": [], + "users": [ + { + "hashing_algorithm": "rabbit_password_hashing_sha256", + "name": "root", + "password_hash": "Y0AhwjtK6iQM0t0Hp9t9nBey8zzMBa3LuszL3zlZ1mGtWsif", + "tags": "administrator" + } + ], + "vhosts": [ + { + "limits": [], + "metadata": { + "description": "Default virtual host", + "tags": [] + }, + "name": "/" + } + ] +} diff --git a/docker-compose.yml b/docker-compose.yml index 46e149f..3093ea1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,22 @@ # For more information: https://laravel.com/docs/sail version: '3' services: + rabbitmq: + image: "rabbitmq:3-management" + ports: + - 5672:5672 + - 15672:15672 + environment: + RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG" + RABBITMQ_DEFAULT_USER: "root" + RABBITMQ_DEFAULT_PASS: "root" + RABBITMQ_DEFAULT_VHOST: "/" + networks: + - sail + volumes: + - ./definitions.json:/etc/rabbitmq/definitions.json + depends_on: + - laravel minio: image: minio/minio command: server /data @@ -11,6 +27,8 @@ services: MINIO_SECRET_KEY: minioroot networks: - sail + depends_on: + - laravel commander: image: rediscommander/redis-commander:latest environment: @@ -25,6 +43,8 @@ services: - redis networks: - sail + depends_on: + - redis redis: image: redis:latest ports: @@ -34,7 +54,7 @@ services: - redis-data:/data networks: - sail - laravel.test: + laravel: build: context: ./vendor/laravel/sail/runtimes/8.0 dockerfile: Dockerfile @@ -68,7 +88,7 @@ services: - sail healthcheck: test: ["CMD", "mysqladmin", "ping"] - myadmin: + pma: image: 'phpmyadmin:latest' ports: - 8080:80 diff --git a/routes/api.php b/routes/api.php index c92d4b3..ced6847 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,6 @@ group(['prefix' => 'actions'], function () use ($router) { $router->group(['prefix' => 'businesses'], function () use ($router) { @@ -165,36 +165,36 @@ $router->group(['prefix' => 'users'], function () use ($router) { }); $router->group([], function () use ($router) { $router->post('/log', 'ActivityController@store'); - $router->group(['prefix' => 'businesses/{business}', 'middleware' => ['auth', 'bindBusiness']], function (Router $router) { + $router->group(['prefix' => 'businesses/{business}', 'middleware' => ['auth', 'bindBusiness']], function ($router) { $router->get('/tasks', 'TaskController@index'); $router->get('/works', 'WorkController@index'); $router->get('statistics', 'StatisticController@index'); $router->get('projects/{project}/statistics', 'StatisticController@index'); $router->get('/tasks/{task}', 'TaskController@show'); - $router->group(['prefix' => 'projects/{project}/tasks'], function (Router $router) { + $router->group(['prefix' => 'projects/{project}/tasks'], function ($router) { $router->post('/', 'TaskController@store'); - $router->group(['prefix' => '{task}'], function (Router $router) { + $router->group(['prefix' => '{task}'], function ($router) { $router->get('/', 'TaskController@show'); $router->put('/', 'TaskController@update'); $router->delete('/', 'TaskController@destroy'); $router->post('/watchers', 'TaskController@toggleWatcher'); - $router->group(['prefix' => 'works'], function (Router $router) { + $router->group(['prefix' => 'works'], function ($router) { // $router->get('/', 'WorkController@index'); $router->post('/', 'WorkController@store'); - $router->group(['prefix' => '{work}'], function (Router $router) { + $router->group(['prefix' => '{work}'], function ($router) { $router->get('/', 'WorkController@show'); $router->put('/', 'WorkController@update'); $router->delete('/', 'WorkController@destroy'); }); }); - $router->group(['prefix' => 'comments'], function (Router $router) { + $router->group(['prefix' => 'comments'], function ($router) { $router->get('/', 'CommentController@index'); $router->post('/', 'CommentController@store'); - $router->group(['prefix' => '{comment}'], function (Router $router) { + $router->group(['prefix' => '{comment}'], function ($router) { $router->get('/', 'CommentController@show'); $router->put('/', 'CommentController@update'); $router->delete('/', 'CommentController@destroy'); From c96af7fd78f6783b4ce53a5e5dcaa08b1d62a39c Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Tue, 2 Mar 2021 16:54:49 +0330 Subject: [PATCH 10/51] Nobody had ever created a function like this one before. --- app/Models/Activity.php | 3 +- app/Models/Business.php | 4 +-- app/Models/Comment.php | 2 +- app/Models/Cost.php | 2 +- app/Models/File.php | 2 +- app/Models/Fingerprint.php | 2 +- app/{Utilities => }/Models/Model.php | 2 +- app/Models/Project.php | 4 +-- .../Models/ReportableRelation.php | 0 app/Models/Sprint.php | 5 +--- app/Models/Status.php | 4 +-- app/Models/System.php | 5 +--- app/Models/Tag.php | 4 +-- app/Models/TagTask.php | 2 +- app/Models/Task.php | 18 ++++++------ app/Models/Transaction.php | 4 +-- app/Models/User.php | 4 +-- app/Models/Work.php | 6 ++-- app/Models/Workflow.php | 3 +- app/Utilities/Helpers/enum.php | 28 ++++++++++++++++++- composer.json | 4 +++ database/factories/BusinessFactory.php | 3 +- database/factories/CostFactory.php | 2 +- database/factories/FileFactory.php | 2 +- database/factories/FingerprintFactory.php | 3 +- database/factories/ProjectFactory.php | 5 ++-- database/factories/SprintflowFactory.php | 4 +-- database/factories/SystemFactory.php | 4 +-- database/factories/TagFactory.php | 2 +- database/factories/TaskFactory.php | 2 +- database/factories/TransactionFactory.php | 2 +- database/factories/UserFactory.php | 2 +- database/factories/WorkFactory.php | 3 +- database/factories/WorkflowFactory.php | 4 +-- database/factories/WorkstatusFactory.php | 2 +- database/seeds/BusinessSeeder.php | 12 ++++---- database/seeds/CostSeeder.php | 2 +- database/seeds/DatabaseSeeder.php | 3 -- database/seeds/FileSeeder.php | 13 --------- database/seeds/ProjectSeeder.php | 6 ++-- database/seeds/SprintSeeder.php | 2 +- database/seeds/TagSeeder.php | 7 ++--- database/seeds/TaskSeeder.php | 4 +-- database/seeds/TransactionSeeder.php | 2 +- database/seeds/UserSeeder.php | 6 ++-- database/seeds/WorkSeeder.php | 2 +- database/seeds/WorkflowSeeder.php | 5 ++-- routes/api.php | 10 +++---- 48 files changed, 107 insertions(+), 115 deletions(-) rename app/{Utilities => }/Models/Model.php (99%) rename app/{Utilities => }/Models/ReportableRelation.php (100%) delete mode 100644 database/seeds/FileSeeder.php diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 29e7ef2..805e4a8 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -2,8 +2,7 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; - +use App\Models\Model; class Activity extends Model { diff --git a/app/Models/Business.php b/app/Models/Business.php index 4d6aa0e..671366c 100644 --- a/app/Models/Business.php +++ b/app/Models/Business.php @@ -3,8 +3,8 @@ namespace App\Models; use App\File; -use App\SoftDeletes; -use App\HiLib\Models\Model; +use App\Models\Model; +use App\Models\SoftDeletes; use Illuminate\Validation\Rule; use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; diff --git a/app/Models/Comment.php b/app/Models/Comment.php index b392b45..ea1c5f8 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -2,8 +2,8 @@ namespace App\Models; +use App\Models\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; class Comment extends Model { diff --git a/app/Models/Cost.php b/app/Models/Cost.php index 8483331..fa06239 100644 --- a/app/Models/Cost.php +++ b/app/Models/Cost.php @@ -2,7 +2,7 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Model; +use App\Models\Model; class Cost extends Model { diff --git a/app/Models/File.php b/app/Models/File.php index 11f2cd4..2c4c56d 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\HiLib\Models\Model; +use App\Models\Model; use Illuminate\Support\Facades\Storage; class File extends Model diff --git a/app/Models/Fingerprint.php b/app/Models/Fingerprint.php index 34254f2..8db0c86 100644 --- a/app/Models/Fingerprint.php +++ b/app/Models/Fingerprint.php @@ -2,7 +2,7 @@ namespace App\Models; -use App\HiLib\Models\Model; +use App\Models\Model; class Fingerprint extends Model { diff --git a/app/Utilities/Models/Model.php b/app/Models/Model.php similarity index 99% rename from app/Utilities/Models/Model.php rename to app/Models/Model.php index 1dd9b0f..db30979 100644 --- a/app/Utilities/Models/Model.php +++ b/app/Models/Model.php @@ -1,6 +1,6 @@ contains(".") ? "" : "."; // the first parameter of all enum keys are its filename [$filename, $key] = explode(".", $key, 2); // because we do not want to load the file every time use require $enums = require __DIR__ . "/../Enums/$filename.php"; // if the key that user provided not exists then null return $enums = Arr::get($enums, $key, null); // if enum null means that key not found throw_if($enums === null, 'Exception', "Undefined enum '{$key}'"); // if enum value is array its mean that user want to use it as collection return is_array($enums) ? collect($enums) : $enums; } } \ No newline at end of file +contains(".") ? "" : "."; + + // the first parameter of all enum keys are its filename + [$filename, $key] = explode(".", $key, 2); + + // because we do not want to load the file every time use require + $enums = require app_path("Enums/$filename.php"); + + // if the key that user provided not exists then null return + $enums = Arr::get($enums, $key, null); + + // if enum null means that key not found + throw_if($enums === null, 'Exception', "Undefined enum '{$key}'"); + + // if enum value is array its mean that user want to use it as collection + return is_array($enums) ? collect($enums) : $enums; + } +} diff --git a/composer.json b/composer.json index 284899a..566fa2d 100644 --- a/composer.json +++ b/composer.json @@ -56,6 +56,10 @@ }, "files": [ "app/Utilities/Helpers/index.php" + ], + "classmap": [ + "database/seeds", + "database/factories" ] }, "autoload-dev": { diff --git a/database/factories/BusinessFactory.php b/database/factories/BusinessFactory.php index 8a15574..053de00 100644 --- a/database/factories/BusinessFactory.php +++ b/database/factories/BusinessFactory.php @@ -2,10 +2,9 @@ /** @var Factory $factory */ -use App\Business; +use App\Models\Business; use Illuminate\Support\Str; use Faker\Generator as Faker; -use Illuminate\Database\Eloquent\Factory; $factory->define(Business::class, function (Faker $faker) { return [ diff --git a/database/factories/CostFactory.php b/database/factories/CostFactory.php index 649cc16..6e84128 100644 --- a/database/factories/CostFactory.php +++ b/database/factories/CostFactory.php @@ -2,8 +2,8 @@ /** @var Factory $factory */ -use App\Cost; use Carbon\Carbon; +use App\Models\Cost; use Faker\Generator as Faker; $factory->define(Cost::class, function (Faker $faker) { diff --git a/database/factories/FileFactory.php b/database/factories/FileFactory.php index c8a4620..45191da 100644 --- a/database/factories/FileFactory.php +++ b/database/factories/FileFactory.php @@ -2,7 +2,7 @@ /** @var Factory $factory */ -use App\File; +use App\Models\File; use Faker\Generator as Faker; $factory->define(File::class, function (Faker $faker) { diff --git a/database/factories/FingerprintFactory.php b/database/factories/FingerprintFactory.php index bbd8a79..d8458e0 100644 --- a/database/factories/FingerprintFactory.php +++ b/database/factories/FingerprintFactory.php @@ -2,9 +2,8 @@ /** @var Factory $factory */ -use App\Fingerprint; +use App\Models\Fingerprint; use Faker\Factory as Faker; -use Illuminate\Database\Eloquent\Factory; use Illuminate\Support\Arr; use Illuminate\Support\Str; diff --git a/database/factories/ProjectFactory.php b/database/factories/ProjectFactory.php index 6dd5c09..d7dce12 100644 --- a/database/factories/ProjectFactory.php +++ b/database/factories/ProjectFactory.php @@ -2,10 +2,9 @@ /** @var Factory $factory */ -use App\Project; -use Faker\Generator as Faker; -use Illuminate\Database\Eloquent\Factory; +use App\Models\Project; use Illuminate\Support\Str; +use Faker\Generator as Faker; $factory->define(Project::class, function (Faker $faker) { return [ diff --git a/database/factories/SprintflowFactory.php b/database/factories/SprintflowFactory.php index ab520fb..b5c3031 100644 --- a/database/factories/SprintflowFactory.php +++ b/database/factories/SprintflowFactory.php @@ -2,13 +2,13 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Sprint; +use App\Models\Sprint; use Faker\Generator as Faker; $factory->define(Sprint::class, function (Faker $faker) { return [ 'business_id' => null, - 'name' => $faker->randomElement(['scrum', 'printing', + 'name' => $faker->randomElement(['scrum', 'printing', 'agile', 'develop', 'design', 'writing', 'seo', 'sale']), 'active' => rand(0, 1), 'description' => $faker->paragraph, diff --git a/database/factories/SystemFactory.php b/database/factories/SystemFactory.php index 0ea6223..0631afb 100644 --- a/database/factories/SystemFactory.php +++ b/database/factories/SystemFactory.php @@ -2,10 +2,8 @@ /** @var Factory $factory */ -use App\System; +use App\Models\System; use Faker\Generator as Faker; -use Illuminate\Database\Eloquent\Factory; -use Illuminate\Support\Str; $factory->define(System::class, function (Faker $faker) { return [ diff --git a/database/factories/TagFactory.php b/database/factories/TagFactory.php index d6779b4..5941665 100644 --- a/database/factories/TagFactory.php +++ b/database/factories/TagFactory.php @@ -2,8 +2,8 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Tag; use Faker\Factory; +use App\Models\Tag; $faker = Factory::create('fa_IR'); $factory->define(Tag::class, function () use ($faker) { diff --git a/database/factories/TaskFactory.php b/database/factories/TaskFactory.php index 713b2cf..66c399a 100644 --- a/database/factories/TaskFactory.php +++ b/database/factories/TaskFactory.php @@ -2,7 +2,7 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Task; +use App\Models\Task; use Faker\Generator as Faker; $factory->define(Task::class, function (Faker $faker) { diff --git a/database/factories/TransactionFactory.php b/database/factories/TransactionFactory.php index a2f346b..80a58ff 100644 --- a/database/factories/TransactionFactory.php +++ b/database/factories/TransactionFactory.php @@ -3,7 +3,7 @@ /** @var Factory $factory */ use Carbon\Carbon; -use App\Transaction; +use App\Models\Transaction; use Faker\Generator as Faker; use Illuminate\Support\Facades\DB; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 871ca9d..779f795 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,7 +2,7 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\User; +use App\Models\User; use Faker\Generator as Faker; $factory->define(User::class, function (Faker $faker) { diff --git a/database/factories/WorkFactory.php b/database/factories/WorkFactory.php index d4e9e2c..f593e38 100644 --- a/database/factories/WorkFactory.php +++ b/database/factories/WorkFactory.php @@ -2,8 +2,7 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Work; -use Carbon\Carbon; +use App\Models\Work; use Faker\Generator as Faker; $factory->define(Work::class, function (Faker $faker) { diff --git a/database/factories/WorkflowFactory.php b/database/factories/WorkflowFactory.php index 72cde2f..4d51b1c 100644 --- a/database/factories/WorkflowFactory.php +++ b/database/factories/WorkflowFactory.php @@ -2,13 +2,13 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Workflow; +use App\Models\Workflow; use Faker\Generator as Faker; $factory->define(Workflow::class, function (Faker $faker) { return [ 'business_id' => null, - 'name' => $faker->randomElement(['scrum', 'printing', + 'name' => $faker->randomElement(['scrum', 'printing', 'agile', 'develop', 'design', 'writing', 'seo', 'sale']), 'desc' => $faker->sentences(1, true), ]; diff --git a/database/factories/WorkstatusFactory.php b/database/factories/WorkstatusFactory.php index 3d2f791..0c5f1fa 100644 --- a/database/factories/WorkstatusFactory.php +++ b/database/factories/WorkstatusFactory.php @@ -2,7 +2,7 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use App\Status; +use App\Models\Status; use Faker\Generator as Faker; $factory->define(Status::class, function (Faker $faker) { diff --git a/database/seeds/BusinessSeeder.php b/database/seeds/BusinessSeeder.php index f622594..1f39038 100644 --- a/database/seeds/BusinessSeeder.php +++ b/database/seeds/BusinessSeeder.php @@ -1,13 +1,13 @@ raw()); $this->call([ UserSeeder::class, BusinessSeeder::class, @@ -18,7 +16,6 @@ class DatabaseSeeder extends Seeder CostSeeder::class, // ProjectSeeder::class, // TaskSeeder::class, - // FileSeeder::class ]); } } diff --git a/database/seeds/FileSeeder.php b/database/seeds/FileSeeder.php deleted file mode 100644 index e5f927a..0000000 --- a/database/seeds/FileSeeder.php +++ /dev/null @@ -1,13 +0,0 @@ -raw(); - } -} diff --git a/database/seeds/ProjectSeeder.php b/database/seeds/ProjectSeeder.php index 5c91c26..70e0bec 100644 --- a/database/seeds/ProjectSeeder.php +++ b/database/seeds/ProjectSeeder.php @@ -1,9 +1,9 @@ raw([ 'business_id' => $business_id, ]); diff --git a/database/seeds/TaskSeeder.php b/database/seeds/TaskSeeder.php index f138ee0..cc746e6 100755 --- a/database/seeds/TaskSeeder.php +++ b/database/seeds/TaskSeeder.php @@ -1,7 +1,7 @@ group(['prefix' => 'actions'], function () use ($router) { }); }); +$router->get('/callback', 'CreditController@callback'); +$router->get('/{transaction}/redirection', 'CreditController@redirection'); + $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth'); $router->delete('/', 'AuthController@delete'); @@ -28,8 +31,6 @@ $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('google/callback', 'AuthController@handleGoogleCallback'); }); - - $router->group(['prefix' => 'businesses'], function () use ($router) { $router->get('/', 'BusinessController@index'); $router->post('/', 'BusinessController@store'); @@ -149,9 +150,6 @@ $router->group(['prefix' => 'businesses'], function () use ($router) { }); }); -$router->get('/callback', 'CreditController@callback'); -$router->get('/{transaction}/redirection', 'CreditController@redirection'); - $router->group(['prefix' => 'users'], function () use ($router) { $router->get('/', 'UserController@index'); $router->get('/search', 'UserController@search'); @@ -163,6 +161,7 @@ $router->group(['prefix' => 'users'], function () use ($router) { $router->delete('/avatar', 'UserController@unSetAvatar'); }); }); + $router->group([], function () use ($router) { $router->post('/log', 'ActivityController@store'); $router->group(['prefix' => 'businesses/{business}', 'middleware' => ['auth', 'bindBusiness']], function ($router) { @@ -182,7 +181,6 @@ $router->group([], function () use ($router) { $router->post('/watchers', 'TaskController@toggleWatcher'); $router->group(['prefix' => 'works'], function ($router) { -// $router->get('/', 'WorkController@index'); $router->post('/', 'WorkController@store'); $router->group(['prefix' => '{work}'], function ($router) { $router->get('/', 'WorkController@show'); From 176458893de06e3ea801743162a07b43326d6929 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 17:34:34 +0330 Subject: [PATCH 11/51] merge task, user routes --- docker-compose.yml | 6 ++-- routes/api.php | 78 +++++++++++++++++++++------------------------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3093ea1..96ea2ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: volumes: - ./definitions.json:/etc/rabbitmq/definitions.json depends_on: - - laravel + - laravel.test minio: image: minio/minio command: server /data @@ -28,7 +28,7 @@ services: networks: - sail depends_on: - - laravel + - laravel.test commander: image: rediscommander/redis-commander:latest environment: @@ -54,7 +54,7 @@ services: - redis-data:/data networks: - sail - laravel: + laravel.test: build: context: ./vendor/laravel/sail/runtimes/8.0 dockerfile: Dockerfile diff --git a/routes/api.php b/routes/api.php index 39ab57b..463c255 100644 --- a/routes/api.php +++ b/routes/api.php @@ -12,6 +12,7 @@ $router->group(['prefix' => 'actions'], function () use ($router) { $router->get('/callback', 'CreditController@callback'); $router->get('/{transaction}/redirection', 'CreditController@redirection'); +$router->post('/log', 'ActivityController@store'); $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth'); @@ -37,6 +38,9 @@ $router->group(['prefix' => 'businesses'], function () use ($router) { $router->group(['prefix' => '{business}', 'middleware' => 'bindBusiness'], function () use ($router) { + $router->get('/tasks', 'TaskController@index'); + $router->get('/works', 'WorkController@index'); + $router->get('statistics', 'StatisticController@index'); $router->put('/avatar', 'BusinessController@setAvatar'); $router->delete('/avatar', 'BusinessController@unSetAvatar'); @@ -61,6 +65,7 @@ $router->group(['prefix' => 'businesses'], function () use ($router) { $router->put('/avatar', 'ProjectController@setAvatar'); $router->delete('/avatar', 'ProjectController@unSetAvatar'); + $router->get('/statistics', 'StatisticController@index'); $router->get('/', 'ProjectController@show'); $router->put('/', 'ProjectController@update'); @@ -109,6 +114,37 @@ $router->group(['prefix' => 'businesses'], function () use ($router) { }); }); + $router->group(['prefix' => 'tasks'], function ($router) { + $router->post('/', 'TaskController@store'); + $router->group(['prefix' => '{task}'], function ($router) { + $router->get('/', 'TaskController@show'); + $router->put('/', 'TaskController@update'); + $router->delete('/', 'TaskController@destroy'); + + $router->post('/watchers', 'TaskController@toggleWatcher'); + + $router->group(['prefix' => 'works'], function ($router) { + $router->post('/', 'WorkController@store'); + $router->group(['prefix' => '{work}'], function ($router) { + $router->get('/', 'WorkController@show'); + $router->put('/', 'WorkController@update'); + $router->delete('/', 'WorkController@destroy'); + }); + }); + + $router->group(['prefix' => 'comments'], function ($router) { + $router->get('/', 'CommentController@index'); + $router->post('/', 'CommentController@store'); + $router->group(['prefix' => '{comment}'], function ($router) { + $router->get('/', 'CommentController@show'); + $router->put('/', 'CommentController@update'); + $router->delete('/', 'CommentController@destroy'); + }); + }); + + }); + }); + }); }); @@ -161,45 +197,3 @@ $router->group(['prefix' => 'users'], function () use ($router) { $router->delete('/avatar', 'UserController@unSetAvatar'); }); }); - -$router->group([], function () use ($router) { - $router->post('/log', 'ActivityController@store'); - $router->group(['prefix' => 'businesses/{business}', 'middleware' => ['auth', 'bindBusiness']], function ($router) { - $router->get('/tasks', 'TaskController@index'); - $router->get('/works', 'WorkController@index'); - $router->get('statistics', 'StatisticController@index'); - $router->get('projects/{project}/statistics', 'StatisticController@index'); - $router->get('/tasks/{task}', 'TaskController@show'); - $router->group(['prefix' => 'projects/{project}/tasks'], function ($router) { - $router->post('/', 'TaskController@store'); - - $router->group(['prefix' => '{task}'], function ($router) { - $router->get('/', 'TaskController@show'); - $router->put('/', 'TaskController@update'); - $router->delete('/', 'TaskController@destroy'); - - $router->post('/watchers', 'TaskController@toggleWatcher'); - - $router->group(['prefix' => 'works'], function ($router) { - $router->post('/', 'WorkController@store'); - $router->group(['prefix' => '{work}'], function ($router) { - $router->get('/', 'WorkController@show'); - $router->put('/', 'WorkController@update'); - $router->delete('/', 'WorkController@destroy'); - }); - }); - - $router->group(['prefix' => 'comments'], function ($router) { - $router->get('/', 'CommentController@index'); - $router->post('/', 'CommentController@store'); - $router->group(['prefix' => '{comment}'], function ($router) { - $router->get('/', 'CommentController@show'); - $router->put('/', 'CommentController@update'); - $router->delete('/', 'CommentController@destroy'); - }); - }); - - }); - }); - }); -}); From fe4dd7f8dccf5cd90f58979969456fb3db3b7da7 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Tue, 2 Mar 2021 18:17:32 +0330 Subject: [PATCH 12/51] =?UTF-8?q?I=20know,=20I=20know,=20this=20is=20not?= =?UTF-8?q?=20how=20I=E2=80=99m=20supposed=20to=20do=20it,=20but=20I=20can?= =?UTF-8?q?'t=20think=20of=20something=20better.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Commands/CostCommand.php | 2 +- app/Enums/log.php | 26 +++- app/Http/Controllers/AuthController.php | 6 +- app/Http/Controllers/BusinessController.php | 6 +- app/Http/Controllers/CommentController.php | 2 +- app/Http/Controllers/Controller.php | 4 +- app/Http/Controllers/FileController.php | 8 +- app/Http/Controllers/InvoiceController.php | 2 +- app/Http/Controllers/ProjectController.php | 2 +- app/Http/Controllers/SprintController.php | 2 +- app/Http/Controllers/StatisticController.php | 2 +- app/Http/Controllers/StatusController.php | 2 +- app/Http/Controllers/SystemController.php | 2 +- app/Http/Controllers/TagController.php | 2 +- app/Http/Controllers/TaskController.php | 10 +- app/Http/Controllers/TaskFileController.php | 6 +- app/Http/Controllers/UserController.php | 4 +- app/Http/Controllers/WorkController.php | 4 +- app/Http/Controllers/WorkflowController.php | 2 +- app/Http/Kernel.php | 2 + app/Providers/AuthServiceProvider.php | 2 +- app/Providers/RouteServiceProvider.php | 2 +- app/Utilities/BusinessInfoRequestMixin.php | 2 +- .../Middlewares/BindBusinessInfo.php | 7 +- .../Providers/AuthServiceProvider.php | 2 +- composer.json | 3 +- composer.lock | 65 +++++++++- config/api-postman.php | 122 ++++++++++++++++++ routes/api.php | 3 +- 29 files changed, 256 insertions(+), 48 deletions(-) create mode 100644 config/api-postman.php diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php index 1ad1095..99a9db5 100644 --- a/app/Console/Commands/CostCommand.php +++ b/app/Console/Commands/CostCommand.php @@ -3,7 +3,7 @@ namespace App\Console\Commands; use DB; -use App\Business; +use App\Models\Business; use Illuminate\Console\Command; use Illuminate\Support\Facades\Cache; diff --git a/app/Enums/log.php b/app/Enums/log.php index a49f08d..3157502 100644 --- a/app/Enums/log.php +++ b/app/Enums/log.php @@ -1 +1,25 @@ - [ User::class => 10, Business::class => 20, Project::class => 30, Task::class => 40, Spenthour::class => 50, ], 'actions' => [ 'created' => 10, 'updated' => 20, 'deleted' => 30, 'restored' => 40, ], ]; \ No newline at end of file + [ + User::class => 10, + Business::class => 20, + Project::class => 30, + Task::class => 40, + Spenthour::class => 50, + ], + + 'actions' => [ + 'created' => 10, + 'updated' => 20, + 'deleted' => 30, + 'restored' => 40, + ], +]; diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 6d41a8d..dae25ac 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers; -use App\User; -use App\Business; -use App\Fingerprint; +use App\Models\User; +use App\Models\Business; +use App\Models\Fingerprint; use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Validation\Rule; diff --git a/app/Http/Controllers/BusinessController.php b/app/Http/Controllers/BusinessController.php index 179837c..aafdd41 100644 --- a/app/Http/Controllers/BusinessController.php +++ b/app/Http/Controllers/BusinessController.php @@ -2,11 +2,11 @@ namespace App\Http\Controllers; -use App\User; -use App\Business; +use App\Models\User; +use App\Models\Business; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Auth; class BusinessController extends Controller { diff --git a/app/Http/Controllers/CommentController.php b/app/Http/Controllers/CommentController.php index 116bf70..898d232 100644 --- a/app/Http/Controllers/CommentController.php +++ b/app/Http/Controllers/CommentController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Models\Comment; use App\Models\Task; +use App\Models\Comment; use Illuminate\Http\Request; use Illuminate\Http\Response; diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index a0a2a8a..03e02a2 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,10 +2,10 @@ namespace App\Http\Controllers; -use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; -use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use Illuminate\Foundation\Validation\ValidatesRequests; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; class Controller extends BaseController { diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index b57455c..88eac5b 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; use App\File; use App\Project; -use App\Business; -use App\Rules\maxBound; +use App\Models\Business; +use App\Rules\MaxBound; use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; @@ -32,8 +32,8 @@ class FileController extends Controller { $bound = 10; $this->validate($request, [ - 'filter.project_id' => [new maxBound($bound)] , - 'filter.user_id' => [new maxBound($bound)] , + 'filter.project_id' => [new MaxBound($bound)] , + 'filter.user_id' => [new MaxBound($bound)] , ]); } diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 1bf3ea2..c277488 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\Cost; -use App\Business; +use App\Models\Business; use Illuminate\Http\Request; use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\AllowedFilter; diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 707625d..5822f40 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\Project; -use App\Business; +use App\Models\Business; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Controllers/SprintController.php b/app/Http/Controllers/SprintController.php index 7f26dcd..46bcbf1 100644 --- a/app/Http/Controllers/SprintController.php +++ b/app/Http/Controllers/SprintController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; use App\Sprint; +use App\Models\Business; use Illuminate\Http\Request; class SprintController extends Controller diff --git a/app/Http/Controllers/StatisticController.php b/app/Http/Controllers/StatisticController.php index 572aae5..27ed05d 100644 --- a/app/Http/Controllers/StatisticController.php +++ b/app/Http/Controllers/StatisticController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use Illuminate\Http\Request; use Illuminate\Support\Arr; +use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class StatisticController extends Controller diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 7740503..6270132 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; use App\Status; +use App\Models\Business; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Validation\Rule; diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index aadc34b..a5d9436 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Business; use App\System; +use App\Models\Business; use Illuminate\Http\Request; class SystemController extends Controller diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 7d661ba..83dd849 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; use App\Tag; +use App\Models\Business; use App\Workflow; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index f447782..9093701 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -2,18 +2,18 @@ namespace App\Http\Controllers; -use App\Http\Resources\TaskCollection; -use App\Http\Resources\TaskResource; -use App\Rules\maxBound; use App\TagTask; +use Carbon\Carbon; use App\Models\Task; use App\Models\Work; -use Carbon\Carbon; +use App\Rules\maxBound; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; -use Spatie\QueryBuilder\AllowedFilter; +use App\Http\Resources\TaskResource; use Spatie\QueryBuilder\QueryBuilder; +use App\Http\Resources\TaskCollection; +use Spatie\QueryBuilder\AllowedFilter; class TaskController extends Controller { diff --git a/app/Http/Controllers/TaskFileController.php b/app/Http/Controllers/TaskFileController.php index ab35061..b29c048 100644 --- a/app/Http/Controllers/TaskFileController.php +++ b/app/Http/Controllers/TaskFileController.php @@ -6,13 +6,13 @@ use Auth; use App\File; use App\Task; use App\Project; -use App\Business; -use Illuminate\Http\Exceptions\HttpResponseException; +use App\Models\Business; use Illuminate\Http\Request; +use mysql_xdevapi\Exception; use App\Http\Controllers\Controller; use App\Http\Resources\FileResource; -use mysql_xdevapi\Exception; use Symfony\Component\HttpFoundation\Response; +use Illuminate\Http\Exceptions\HttpResponseException; class TaskFileController extends Controller { diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index fba4e8d..ceded14 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -4,10 +4,10 @@ namespace App\Http\Controllers; -use App\User; +use App\Models\User; use Illuminate\Http\Request; -use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; class UserController extends Controller { diff --git a/app/Http/Controllers/WorkController.php b/app/Http/Controllers/WorkController.php index a2fa6ba..89b00f9 100644 --- a/app/Http/Controllers/WorkController.php +++ b/app/Http/Controllers/WorkController.php @@ -2,13 +2,13 @@ namespace App\Http\Controllers; +use Carbon\Carbon; use App\Models\Task; use App\Models\Work; -use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Http\Response; -use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\QueryBuilder; +use Spatie\QueryBuilder\AllowedFilter; class WorkController extends Controller { diff --git a/app/Http/Controllers/WorkflowController.php b/app/Http/Controllers/WorkflowController.php index 80fdc3d..faaa219 100644 --- a/app/Http/Controllers/WorkflowController.php +++ b/app/Http/Controllers/WorkflowController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; use App\Status; +use App\Models\Business; use App\Workflow; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 30020a5..4bd9d18 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,6 +2,7 @@ namespace App\Http; +use App\Utilities\Middlewares\BindBusinessInfo; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel @@ -62,5 +63,6 @@ class Kernel extends HttpKernel 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'bindBusiness' => BindBusinessInfo::class, ]; } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index a5cc37f..7936e8d 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,7 +2,7 @@ namespace App\Providers; -use App\Fingerprint; +use App\Models\Fingerprint; use App\Utilities\RequestMixin; use App\Utilities\BusinessInfoRequestMixin; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 3bd3c81..927be5d 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -26,7 +26,7 @@ class RouteServiceProvider extends ServiceProvider * * @var string|null */ - // protected $namespace = 'App\\Http\\Controllers'; + protected $namespace = 'App\\Http\\Controllers'; /** * Define your route model bindings, pattern filters, etc. diff --git a/app/Utilities/BusinessInfoRequestMixin.php b/app/Utilities/BusinessInfoRequestMixin.php index 5966ae3..2fd1da3 100644 --- a/app/Utilities/BusinessInfoRequestMixin.php +++ b/app/Utilities/BusinessInfoRequestMixin.php @@ -2,7 +2,7 @@ namespace App\Utilities; -use App\User; +use App\Models\User; use Illuminate\Support\Arr; use Jenssegers\Agent\Agent; diff --git a/app/Utilities/Middlewares/BindBusinessInfo.php b/app/Utilities/Middlewares/BindBusinessInfo.php index f49bb95..788a54d 100644 --- a/app/Utilities/Middlewares/BindBusinessInfo.php +++ b/app/Utilities/Middlewares/BindBusinessInfo.php @@ -3,6 +3,7 @@ namespace App\Utilities\Middlewares; use Closure; +use App\Models\Business; class BindBusinessInfo { @@ -10,11 +11,7 @@ class BindBusinessInfo public function handle($request, Closure $next, $guard = null) { $business_id = $request->has('business_id') ? $request->business_id : $request->route('business'); - $businessInfo = env('CONTAINER_NAME') == 'hi-user-app' ? - \App\Business::info($business_id) : - get('user', env('USER_URL') . 'actions/businesses/' . $business_id . '/info', []); - - + $businessInfo = Business::info($business_id); $request->merge(['_business_info' => $businessInfo]); return $next($request); diff --git a/app/Utilities/Providers/AuthServiceProvider.php b/app/Utilities/Providers/AuthServiceProvider.php index a206351..4b25477 100644 --- a/app/Utilities/Providers/AuthServiceProvider.php +++ b/app/Utilities/Providers/AuthServiceProvider.php @@ -2,7 +2,7 @@ namespace App\Utilities\Providers; -use App\User; +use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Auth; diff --git a/composer.json b/composer.json index 566fa2d..ca1dcad 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "laravel/sail": "^1.0.1", "mockery/mockery": "^1.4.2", "nunomaduro/collision": "^5.0", - "phpunit/phpunit": "^9.3.3" + "phpunit/phpunit": "^9.3.3", + "andreaselia/laravel-api-to-postman":"^1.0" }, "config": { "optimize-autoloader": true, diff --git a/composer.lock b/composer.lock index d8a10cf..91600a6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f9d97bc77997badf02ebb4ff03402453", + "content-hash": "e612fae4cbef3c536c8e43cb7732d4cb", "packages": [ { "name": "anik/amqp", @@ -6832,6 +6832,69 @@ } ], "packages-dev": [ + { + "name": "andreaselia/laravel-api-to-postman", + "version": "v1.7.2", + "source": { + "type": "git", + "url": "https://github.com/AndreasElia/laravel-api-to-postman.git", + "reference": "c40892559137ac356268afb03d3e279ff3efb188" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/AndreasElia/laravel-api-to-postman/zipball/c40892559137ac356268afb03d3e279ff3efb188", + "reference": "c40892559137ac356268afb03d3e279ff3efb188", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/config": "^6.0|^7.0|^8.0", + "illuminate/console": "^6.0|^7.0|^8.0", + "illuminate/contracts": "^6.0|^7.0|^8.0", + "illuminate/routing": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "orchestra/testbench": "^6.12" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "AndreasElia\\PostmanGenerator\\PostmanGeneratorServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "AndreasElia\\PostmanGenerator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Elia", + "email": "andreaselia@live.co.uk" + } + ], + "description": "Generate a Postman collection automatically from your Laravel API", + "keywords": [ + "Postman", + "api", + "collection", + "generate", + "laravel" + ], + "support": { + "issues": "https://github.com/AndreasElia/laravel-api-to-postman/issues", + "source": "https://github.com/AndreasElia/laravel-api-to-postman/tree/v1.7.2" + }, + "time": "2021-02-28T23:49:29+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.0", diff --git a/config/api-postman.php b/config/api-postman.php new file mode 100644 index 0000000..7d9727b --- /dev/null +++ b/config/api-postman.php @@ -0,0 +1,122 @@ + env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Collection Filename + |-------------------------------------------------------------------------- + | + | The name for the collection file to be saved. + | + */ + + 'filename' => '{timestamp}_{app}_collection.json', + + /* + |-------------------------------------------------------------------------- + | Structured + |-------------------------------------------------------------------------- + | + | If you want folders to be generated based on namespace. + | + */ + + 'structured' => true, + + /* + |-------------------------------------------------------------------------- + | Auth Middleware + |-------------------------------------------------------------------------- + | + | The middleware which wraps your authenticated API routes. + | + | E.g. auth:api, auth:sanctum + | + */ + + 'auth_middleware' => 'auth:api', + + /* + |-------------------------------------------------------------------------- + | Headers + |-------------------------------------------------------------------------- + | + | The headers applied to all routes within the collection. + | + */ + + 'headers' => [ + [ + 'key' => 'Accept', + 'value' => 'application/json', + ], + [ + 'key' => 'Content-Type', + 'value' => 'application/json', + ], + [ + 'key' => 'Authorization', + 'value' => 'Bearer SBzUKamo6CrzQD0hzys2zDxeXiHmPnQcCmlmtCgHAo0JRglIApqJuFF0zxIY', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Enable Form Data + |-------------------------------------------------------------------------- + | + | Determines whether or not form data should be handled. + | + */ + + 'enable_formdata' => false, + + /* + |-------------------------------------------------------------------------- + | Form Data + |-------------------------------------------------------------------------- + | + | The key/values to requests for form data dummy information. + | + */ + + 'formdata' => [ + // 'email' => 'john@example.com', + // 'password' => 'changeme', + ], + + /* + |-------------------------------------------------------------------------- + | Include Middleware + |-------------------------------------------------------------------------- + | + | The routes of the included middleware are included in the export. + | + */ + + 'include_middleware' => ['api'], + + /* + |-------------------------------------------------------------------------- + | Disk Driver + |-------------------------------------------------------------------------- + | + | Specify the configured disk for storing the postman collection file. + | + */ + + 'disk' => 'local', + +]; diff --git a/routes/api.php b/routes/api.php index 463c255..aa1192e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -12,7 +12,6 @@ $router->group(['prefix' => 'actions'], function () use ($router) { $router->get('/callback', 'CreditController@callback'); $router->get('/{transaction}/redirection', 'CreditController@redirection'); -$router->post('/log', 'ActivityController@store'); $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth'); @@ -186,7 +185,7 @@ $router->group(['prefix' => 'businesses'], function () use ($router) { }); }); -$router->group(['prefix' => 'users'], function () use ($router) { +$router->group(['prefix' => 'users','middleware' => 'bindBusiness'], function () use ($router) { $router->get('/', 'UserController@index'); $router->get('/search', 'UserController@search'); $router->group(['prefix' => '{user}'], function () use ($router) { From d08258ebb98d1d867b7a4a9caea74608ae0e687e Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 18:21:28 +0330 Subject: [PATCH 13/51] fix namespaces in models, login bug fix --- app/Http/Controllers/AuthController.php | 19 ++++++++++--------- app/Http/Controllers/BusinessController.php | 4 ++-- app/Http/Controllers/CreditController.php | 2 +- app/Http/Controllers/FileController.php | 6 +++--- app/Http/Controllers/InvoiceController.php | 4 ++-- app/Http/Controllers/ProjectController.php | 4 ++-- app/Http/Controllers/SprintController.php | 4 ++-- app/Http/Controllers/StatusController.php | 4 ++-- app/Http/Controllers/SystemController.php | 4 ++-- app/Http/Controllers/TagController.php | 6 +++--- app/Http/Controllers/TaskController.php | 2 +- app/Http/Controllers/TaskFileController.php | 8 ++++---- app/Http/Controllers/UserController.php | 2 +- app/Http/Controllers/WorkflowController.php | 6 +++--- app/Models/Business.php | 4 ++-- app/Models/Project.php | 4 ++-- app/Models/ReportableRelation.php | 2 +- app/Models/Task.php | 2 +- app/Models/User.php | 4 ++-- bootstrap/app.php | 7 +++++++ 20 files changed, 53 insertions(+), 45 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 6d41a8d..b0f2144 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers; -use App\User; -use App\Business; -use App\Fingerprint; +use App\Models\User; +use App\Models\Business; +use App\Models\Fingerprint; use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Validation\Rule; @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Cache; use Laravel\Lumen\Routing\Controller; use Laravel\Socialite\Facades\Socialite; use Illuminate\Session\TokenMismatchException; +use phpDocumentor\Reflection\Location; use Symfony\Component\HttpFoundation\Response; class AuthController extends Controller @@ -69,7 +70,7 @@ class AuthController extends Controller $user = User::where('email', $request->email)->first(); if ($user && Hash::check($request->password, $user->password)) { - Auth::viaRequest('api', fn() => $user); + Auth::setUser($user); return [ 'auth' => $this->createFingerPrint(), @@ -252,11 +253,11 @@ class AuthController extends Controller public function createFingerPrint() { $attributes = [ - 'agent' => \request()->getAgent(), - 'ip' => \request()->getClientIp(), - 'os' => \request()->getOS(), - 'latitude' => \request()->getLocation()->getAttribute('lat'), - 'longitude' => \request()->getLocation()->getAttribute('lon'), + 'agent' => request()->getAgent(), + 'ip' => request()->getClientIp(), + 'os' => request()->getOS(), + 'latitude' => 5, + 'longitude' => 5, ]; $values = [ diff --git a/app/Http/Controllers/BusinessController.php b/app/Http/Controllers/BusinessController.php index 179837c..633f162 100644 --- a/app/Http/Controllers/BusinessController.php +++ b/app/Http/Controllers/BusinessController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\User; -use App\Business; +use App\Models\User; +use App\Models\Business; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Controllers/CreditController.php b/app/Http/Controllers/CreditController.php index e430404..d1f311b 100644 --- a/app/Http/Controllers/CreditController.php +++ b/app/Http/Controllers/CreditController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers; -use App\Transaction; +use App\Models\Transaction; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Spatie\QueryBuilder\QueryBuilder; diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index b57455c..1fc8394 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -2,9 +2,9 @@ namespace App\Http\Controllers; -use App\File; -use App\Project; -use App\Business; +use App\Models\File; +use App\Models\Project; +use App\Models\Business; use App\Rules\maxBound; use Illuminate\Support\Str; use Illuminate\Http\Request; diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 1bf3ea2..b6ee5ac 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Cost; -use App\Business; +use App\Models\Cost; +use App\Models\Business; use Illuminate\Http\Request; use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\AllowedFilter; diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 707625d..4a396ef 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Project; -use App\Business; +use App\Models\Project; +use App\Models\Business; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; diff --git a/app/Http/Controllers/SprintController.php b/app/Http/Controllers/SprintController.php index 7f26dcd..446f780 100644 --- a/app/Http/Controllers/SprintController.php +++ b/app/Http/Controllers/SprintController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; -use App\Sprint; +use App\Models\Business; +use App\Models\Sprint; use Illuminate\Http\Request; class SprintController extends Controller diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 7740503..f5959f5 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers; -use App\Business; -use App\Status; +use App\Models\Business; +use App\Models\Status; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Validation\Rule; diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index aadc34b..b2d5d00 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -2,8 +2,8 @@ namespace App\Http\Controllers; -use App\Business; -use App\System; +use App\Models\Business; +use App\Models\System; use Illuminate\Http\Request; class SystemController extends Controller diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 7d661ba..a00e53f 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -4,9 +4,9 @@ namespace App\Http\Controllers; -use App\Business; -use App\Tag; -use App\Workflow; +use App\Models\Business; +use App\Models\Tag; +use App\Models\Workflow; use Illuminate\Http\Request; class TagController extends Controller diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index f447782..f6e28cd 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -5,7 +5,7 @@ namespace App\Http\Controllers; use App\Http\Resources\TaskCollection; use App\Http\Resources\TaskResource; use App\Rules\maxBound; -use App\TagTask; +use App\Models\TagTask; use App\Models\Task; use App\Models\Work; use Carbon\Carbon; diff --git a/app/Http/Controllers/TaskFileController.php b/app/Http/Controllers/TaskFileController.php index ab35061..3c2c177 100644 --- a/app/Http/Controllers/TaskFileController.php +++ b/app/Http/Controllers/TaskFileController.php @@ -3,10 +3,10 @@ namespace App\Http\Controllers; use Auth; -use App\File; -use App\Task; -use App\Project; -use App\Business; +use App\Models\File; +use App\Models\Task; +use App\Models\Project; +use App\Models\Business; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\Request; use App\Http\Controllers\Controller; diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index fba4e8d..ae42af8 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers; -use App\User; +use App\Models\User; use Illuminate\Http\Request; use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\QueryBuilder; diff --git a/app/Http/Controllers/WorkflowController.php b/app/Http/Controllers/WorkflowController.php index 80fdc3d..27f5c14 100644 --- a/app/Http/Controllers/WorkflowController.php +++ b/app/Http/Controllers/WorkflowController.php @@ -4,9 +4,9 @@ namespace App\Http\Controllers; -use App\Business; -use App\Status; -use App\Workflow; +use App\Models\Business; +use App\Models\Status; +use App\Models\Workflow; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; diff --git a/app/Models/Business.php b/app/Models/Business.php index 671366c..eaab90f 100644 --- a/app/Models/Business.php +++ b/app/Models/Business.php @@ -2,14 +2,14 @@ namespace App\Models; -use App\File; +use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; +use App\Models\ReportableRelation; 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; diff --git a/app/Models/Project.php b/app/Models/Project.php index 7854d46..d9aca59 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -2,13 +2,13 @@ namespace App\Models; -use App\File; +use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; +use App\Models\ReportableRelation; 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; diff --git a/app/Models/ReportableRelation.php b/app/Models/ReportableRelation.php index 0c23b48..b8b5d07 100644 --- a/app/Models/ReportableRelation.php +++ b/app/Models/ReportableRelation.php @@ -1,6 +1,6 @@ singleton( | */ +$app->router->group([ + 'namespace' => 'App\Http\Controllers', + 'prefix' => '/user/v1/' +], function ($router) { + require __DIR__.'/../routes/api.php'; +}); + return $app; From 95b899843b4a5d3a03333b7feceb67002e0f3ddc Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 2 Mar 2021 19:01:30 +0330 Subject: [PATCH 14/51] fix AuthServiceProvider.php --- app/Providers/AuthServiceProvider.php | 3 ++- routes/api.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 7936e8d..c02d1b6 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -30,7 +30,8 @@ class AuthServiceProvider extends ServiceProvider $this->app['request']->mixin(new RequestMixin); $this->app['request']->mixin(new BusinessInfoRequestMixin); - $this->app['auth']->viaRequest('api', function ($request) { + $this->app['auth']->viaRequest('token', function ($request) { + if ($request->bearerToken() === null) { return null; } diff --git a/routes/api.php b/routes/api.php index aa1192e..7e8d9c9 100644 --- a/routes/api.php +++ b/routes/api.php @@ -31,7 +31,7 @@ $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('google/callback', 'AuthController@handleGoogleCallback'); }); -$router->group(['prefix' => 'businesses'], function () use ($router) { +$router->group(['prefix' => 'businesses', 'middleware' => 'auth:api'], function () use ($router) { $router->get('/', 'BusinessController@index'); $router->post('/', 'BusinessController@store'); From 4eb782a0c62ba567048b074745b0b3fdab9b30f1 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 12:45:00 +0330 Subject: [PATCH 15/51] Fix cache locations --- app/Http/Controllers/AuthController.php | 5 ++--- config/geoip.php | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index b0f2144..697f930 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -16,7 +16,6 @@ use Illuminate\Support\Facades\Cache; use Laravel\Lumen\Routing\Controller; use Laravel\Socialite\Facades\Socialite; use Illuminate\Session\TokenMismatchException; -use phpDocumentor\Reflection\Location; use Symfony\Component\HttpFoundation\Response; class AuthController extends Controller @@ -256,8 +255,8 @@ class AuthController extends Controller 'agent' => request()->getAgent(), 'ip' => request()->getClientIp(), 'os' => request()->getOS(), - 'latitude' => 5, - 'longitude' => 5, + 'latitude' => \request()->getLocation()->getAttribute('lat'), + 'longitude' => \request()->getLocation()->getAttribute('lon'), ]; $values = [ diff --git a/config/geoip.php b/config/geoip.php index 0c812fc..62925df 100644 --- a/config/geoip.php +++ b/config/geoip.php @@ -131,7 +131,7 @@ return [ | */ -// 'cache_tags' => [''], + 'cache_tags' => [], /* |-------------------------------------------------------------------------- From ab00fc5609defeaf7b913ff3cf5af944bd3eabda Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 12:46:51 +0330 Subject: [PATCH 16/51] fix amqp and create new seeder --- app/Http/Controllers/TaskController.php | 2 +- app/Models/Task.php | 3 +- config/amqp.php | 73 +++++++++++++++++++++++++ database/factories/BusinessFactory.php | 1 - database/seeds/DatabaseSeeder.php | 5 +- database/seeds/TaskTmpSeeder.php | 33 +++++++++++ 6 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 config/amqp.php create mode 100644 database/seeds/TaskTmpSeeder.php diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index f67b90f..6a9b8de 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -140,7 +140,7 @@ class TaskController extends Controller public function store($business, $project, Request $request) { - permit('projectTasks', ['project_id' => $project]); +// permit('projectTasks', ['project_id' => $project]); $task = Task::create($request->merge( ['business_id' => $business, 'project_id' => $project, 'creator_id' => \auth()->id()] diff --git a/app/Models/Task.php b/app/Models/Task.php index fef0a11..5923ac9 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -42,6 +42,7 @@ class Task extends Model public function rules() { +// dd(\request('_business_info')['info']['workflows']->toArray()); $validations = [ 'assignee_id' => ['nullable', 'numeric', function ($attribute, $value, $fail) { @@ -60,7 +61,7 @@ class Task extends Model 'sprint_id' => ['nullable', 'numeric', Rule::in(\request('_business_info')['info']['projects'][request()->route('project')]['sprints'])], 'workflow_id' => ['required', 'numeric', - Rule::in(array_keys(\request('_business_info')['info']['workflows']))], + Rule::in(array_keys(\request('_business_info')['info']['workflows']->toArray()))], 'status_id' => 'required|numeric', 'approver_id' => ['nullable', 'numeric', function ($attribute, $value, $fail) { diff --git a/config/amqp.php b/config/amqp.php new file mode 100644 index 0000000..6c58ccc --- /dev/null +++ b/config/amqp.php @@ -0,0 +1,73 @@ + env('AMQP_CONNECTION', 'rabbitmq'), + + /*Available connections*/ + 'connections' => [ + + 'rabbitmq' => [ + 'connection' => [ + 'host' => env('AMQP_HOST', 'liwo_rabbitmq_1'), + '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), + ], + ], + ], +]; diff --git a/database/factories/BusinessFactory.php b/database/factories/BusinessFactory.php index 053de00..f8dab3d 100644 --- a/database/factories/BusinessFactory.php +++ b/database/factories/BusinessFactory.php @@ -11,7 +11,6 @@ $factory->define(Business::class, function (Faker $faker) { '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)), ]; diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index c7abe6c..7508ea3 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -12,8 +12,9 @@ class DatabaseSeeder extends Seeder TagSeeder::class, WorkflowSeeder::class, SprintSeeder::class, - TransactionSeeder::class, - CostSeeder::class, +// TransactionSeeder::class, +// CostSeeder::class, + TaskTmpSeeder::class, // ProjectSeeder::class, // TaskSeeder::class, ]); diff --git a/database/seeds/TaskTmpSeeder.php b/database/seeds/TaskTmpSeeder.php new file mode 100644 index 0000000..38da127 --- /dev/null +++ b/database/seeds/TaskTmpSeeder.php @@ -0,0 +1,33 @@ +business_id)->first(); + $sprint = \App\Models\Sprint::where('business_id', $system->business_id)->first(); + $creator = \App\Models\Business::find($system->business_id)->owners()->first(); + + array_push($tasks, [ + 'title' => \Illuminate\Support\Str::random(5), + 'business_id' => $system->business_id, + 'project_id' => $system->project_id, + 'system_id' => $system->id, + 'workflow_id' => $status->workflow_id, + 'status_id' => $status->id, + 'sprint_id' => $sprint->id ?? null, + 'creator_id' => $creator->id, + ]); + if (sizeof($tasks) == 100) { + Task::insert($tasks); + $tasks = []; + } + } + } +} From 4856f07126748c681ab9feb705c627aa03f84164 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 13:21:30 +0330 Subject: [PATCH 17/51] Fix spatie medialibrary --- composer.lock | 60 +++++++++---------- ... 2021_03_03_093326_create_media_table.php} | 3 +- 2 files changed, 32 insertions(+), 31 deletions(-) rename database/migrations/{2023_10_14_085115_create_media_table.php.php => 2021_03_03_093326_create_media_table.php} (89%) diff --git a/composer.lock b/composer.lock index 91600a6..59f8671 100644 --- a/composer.lock +++ b/composer.lock @@ -119,16 +119,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.173.19", + "version": "3.173.20", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "63c6feca49bf4083f33bf250401c0c286759e101" + "reference": "3e6143f5c12986d727307d5d19d6aec21575d903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/63c6feca49bf4083f33bf250401c0c286759e101", - "reference": "63c6feca49bf4083f33bf250401c0c286759e101", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3e6143f5c12986d727307d5d19d6aec21575d903", + "reference": "3e6143f5c12986d727307d5d19d6aec21575d903", "shasum": "" }, "require": { @@ -136,9 +136,9 @@ "ext-pcre": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", - "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4.1", - "mtdowling/jmespath.php": "^2.5", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.7.0", + "mtdowling/jmespath.php": "^2.6", "php": ">=5.5" }, "require-dev": { @@ -203,9 +203,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.173.19" + "source": "https://github.com/aws/aws-sdk-php/tree/3.173.20" }, - "time": "2021-03-01T19:15:59+00:00" + "time": "2021-03-02T19:13:50+00:00" }, { "name": "beberlei/assert", @@ -1313,16 +1313,16 @@ }, { "name": "laravel/framework", - "version": "v8.29.0", + "version": "v8.30.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e" + "reference": "81ef9850cc388f2f92b868fb35ffb76f0c9a0f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d2eba352b3b3a3c515b18c5726b373fe5026733e", - "reference": "d2eba352b3b3a3c515b18c5726b373fe5026733e", + "url": "https://api.github.com/repos/laravel/framework/zipball/81ef9850cc388f2f92b868fb35ffb76f0c9a0f46", + "reference": "81ef9850cc388f2f92b868fb35ffb76f0c9a0f46", "shasum": "" }, "require": { @@ -1477,7 +1477,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-02-23T14:27:41+00:00" + "time": "2021-03-02T14:28:44+00:00" }, { "name": "laravel/helpers", @@ -1690,16 +1690,16 @@ }, { "name": "laravel/socialite", - "version": "v5.2.1", + "version": "v5.2.2", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "8aa705d771f127317f60887515cfb2f777653ce5" + "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/8aa705d771f127317f60887515cfb2f777653ce5", - "reference": "8aa705d771f127317f60887515cfb2f777653ce5", + "url": "https://api.github.com/repos/laravel/socialite/zipball/8d25d574b4f2005411c0b9cb527ef5e745c1b07d", + "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d", "shasum": "" }, "require": { @@ -1755,20 +1755,20 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2021-02-22T14:22:51+00:00" + "time": "2021-03-02T16:50:47+00:00" }, { "name": "laravel/tinker", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88" + "reference": "04ad32c1a3328081097a181875733fa51f402083" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/daae1c43f1300fe88c05d83db6f3d8f76677ad88", - "reference": "daae1c43f1300fe88c05d83db6f3d8f76677ad88", + "url": "https://api.github.com/repos/laravel/tinker/zipball/04ad32c1a3328081097a181875733fa51f402083", + "reference": "04ad32c1a3328081097a181875733fa51f402083", "shasum": "" }, "require": { @@ -1821,9 +1821,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.6.0" + "source": "https://github.com/laravel/tinker/tree/v2.6.1" }, - "time": "2021-01-26T20:35:18+00:00" + "time": "2021-03-02T16:53:12+00:00" }, { "name": "league/commonmark", @@ -7335,16 +7335,16 @@ }, { "name": "laravel/sail", - "version": "v1.4.3", + "version": "v1.4.4", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "0200ce6e0f697699bce036c42d91f1daab8039a8" + "reference": "67b76f430f8870f5a2384db5c87ff8321154e077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/0200ce6e0f697699bce036c42d91f1daab8039a8", - "reference": "0200ce6e0f697699bce036c42d91f1daab8039a8", + "url": "https://api.github.com/repos/laravel/sail/zipball/67b76f430f8870f5a2384db5c87ff8321154e077", + "reference": "67b76f430f8870f5a2384db5c87ff8321154e077", "shasum": "" }, "require": { @@ -7391,7 +7391,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2021-02-24T21:20:16+00:00" + "time": "2021-03-02T16:47:59+00:00" }, { "name": "mockery/mockery", diff --git a/database/migrations/2023_10_14_085115_create_media_table.php.php b/database/migrations/2021_03_03_093326_create_media_table.php similarity index 89% rename from database/migrations/2023_10_14_085115_create_media_table.php.php rename to database/migrations/2021_03_03_093326_create_media_table.php index d0ac271..378b046 100644 --- a/database/migrations/2023_10_14_085115_create_media_table.php.php +++ b/database/migrations/2021_03_03_093326_create_media_table.php @@ -12,7 +12,7 @@ class CreateMediaTable extends Migration $table->bigIncrements('id'); $table->morphs('model'); - $table->uuid('uuid')->nullable(); + $table->uuid('uuid')->nullable()->unique(); $table->string('collection_name'); $table->string('name'); $table->string('file_name'); @@ -22,6 +22,7 @@ class CreateMediaTable extends Migration $table->unsignedBigInteger('size'); $table->json('manipulations'); $table->json('custom_properties'); + $table->json('generated_conversions'); $table->json('responsive_images'); $table->unsignedInteger('order_column')->nullable(); From ba309d9a1f809de3c49bee55153eb1605f428597 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 15:55:35 +0330 Subject: [PATCH 18/51] bug fix work, comment, task cruds during test convert all task relation from tagTask to tags --- app/Http/Controllers/TaskController.php | 24 ++++++++++++------------ app/Http/Controllers/WorkController.php | 6 +++--- app/Http/Resources/TaskResource.php | 2 +- app/Models/Task.php | 6 ++---- app/Models/Work.php | 2 +- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php index 6a9b8de..02ef09b 100644 --- a/app/Http/Controllers/TaskController.php +++ b/app/Http/Controllers/TaskController.php @@ -6,7 +6,7 @@ use Carbon\Carbon; use App\Models\Task; use App\Models\Work; use App\Models\TagTask; -use App\Rules\maxBound; +use App\Rules\MaxBound; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\DB; @@ -30,20 +30,20 @@ class TaskController extends Controller return $request->filled('group') ? $tasks->get()->groupBy($request->group) ->map(function ($q) { return $q->keyBy('status_id'); }) - : /*response()->json(collect(['now' => Carbon::now()->toDateString()])->merge(*/new TaskCollection($tasks->paginate($per_page));//)); + : new TaskCollection($tasks->paginate($per_page)); } public function indexValidation($request) { $bound = 10; $this->validate($request, [ - 'filter.project_id' => [new maxBound($bound)] , - 'filter.creator_id' => [new maxBound($bound)] , - 'filter.assignee_id' => [new maxBound($bound)] , - 'filter.system_id' => [new maxBound($bound)] , - 'filter.workflow_id' => [new maxBound($bound)] , - 'filter.status_id' => [new maxBound($bound)] , - 'filter.approver_id' => [new maxBound($bound)] , + 'filter.project_id' => [new MaxBound($bound)] , + 'filter.creator_id' => [new MaxBound($bound)] , + 'filter.assignee_id' => [new MaxBound($bound)] , + 'filter.system_id' => [new MaxBound($bound)] , + 'filter.workflow_id' => [new MaxBound($bound)] , + 'filter.status_id' => [new MaxBound($bound)] , + 'filter.approver_id' => [new MaxBound($bound)] , 'filter.priority_min' => 'nullable|numeric|between:1,10' , 'filter.priority_max' => 'nullable|numeric|between:1,10' , 'filter.ready_to_test' => 'nullable|boolean' , @@ -55,7 +55,7 @@ class TaskController extends Controller { $query = Task::where('business_id', $business); $taskQ = QueryBuilder::for($query) - ->with('tagTask') +// ->with('tags') ->select(DB::raw('tasks.* , (spent_time - estimated_time) as over_spent')) ->allowedFilters([ AllowedFilter::exact('project_id'), @@ -140,7 +140,7 @@ class TaskController extends Controller public function store($business, $project, Request $request) { -// permit('projectTasks', ['project_id' => $project]); + permit('projectTasks', ['project_id' => $project]); $task = Task::create($request->merge( ['business_id' => $business, 'project_id' => $project, 'creator_id' => \auth()->id()] @@ -167,7 +167,7 @@ class TaskController extends Controller * 1) guest's only can see self task * 2) user is active in project */ - public function show($business, $task, $project =null) + public function show($business, $project, $task) { $task = Task::findOrFail($task); $project = $task->project_id; diff --git a/app/Http/Controllers/WorkController.php b/app/Http/Controllers/WorkController.php index 89b00f9..d7f6d1f 100644 --- a/app/Http/Controllers/WorkController.php +++ b/app/Http/Controllers/WorkController.php @@ -124,7 +124,7 @@ class WorkController extends Controller // 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, // 'spent_time' => $taskModel->spent_time + $diff_in_min // ]); - return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']); + return $taskModel->load(['tags', 'works', 'comments']); } public function storeValidation($request, $taskModel) @@ -187,7 +187,7 @@ class WorkController extends Controller // 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, // 'spent_time' => ($taskModel->spent_time - $old_diff_in_min) + $new_diff_in_min // ]); - return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works', 'comments']); + return $taskModel->load(['tags', 'works', 'comments']); } public function updateValidation($request, $taskModel, $workModel) @@ -237,6 +237,6 @@ class WorkController extends Controller 'work_start' => Work::where('task_id', $taskModel->id)->orderBy('started_at')->first()->started_at ?? null, 'spent_time' => ($taskModel->spent_time - $diff_in_min) ]); - return $taskModel->load(['tagTask'=> fn($q) => $q->select('id', 'tag_id', 'task_id'), 'works','comments']); + return $taskModel->load(['tags', 'works','comments']); } } diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php index 6737a5a..eab56f3 100644 --- a/app/Http/Resources/TaskResource.php +++ b/app/Http/Resources/TaskResource.php @@ -21,7 +21,7 @@ class TaskResource extends JsonResource } } - $resource['tags'] = $this->tagTask()->pluck('tag_id')->toArray(); + $resource['tags'] = $this->tags; $resource['works'] = $this->works; $resource['comments'] = $this->comments; diff --git a/app/Models/Task.php b/app/Models/Task.php index 5923ac9..ffef4f7 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -158,12 +158,10 @@ class Task extends Model public function tags() { - return $this->belongsToMany( - Tag::class, 'tag_task', 'task_id', 'tag_id', - 'id', 'id', __FUNCTION__ - )->using(ReportableRelation::class); + return $this->belongsToMany(Tag::class)->using(ReportableRelation::class); } + // Todo: are we need this relation???? public function tagTask() { return $this->hasMany(TagTask::class, 'task_id', 'id'); diff --git a/app/Models/Work.php b/app/Models/Work.php index c84648e..0b6ce81 100644 --- a/app/Models/Work.php +++ b/app/Models/Work.php @@ -37,7 +37,7 @@ class Work extends Model 'ended_at' => [ 'required', 'date_format:Y-m-d H:i', 'after:'.$started_at, function ($attribute, $value, $fail) use ($ended_at, $started_at) { - $works = \App\Work::where([ + $works = $this::where([ ['ended_at', '>', $started_at], ['ended_at', '<', $ended_at], ])->orWhere([ From 4003c05338d38c5b69e30819c9854fb40a81bf19 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 16:26:48 +0330 Subject: [PATCH 19/51] some bug fix --- app/Http/Controllers/StatisticController.php | 2 +- database/migrations/2020_08_18_085114_create_tags_table.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/StatisticController.php b/app/Http/Controllers/StatisticController.php index 27ed05d..ae65111 100644 --- a/app/Http/Controllers/StatisticController.php +++ b/app/Http/Controllers/StatisticController.php @@ -43,7 +43,7 @@ class StatisticController extends Controller $this->addSystems($result, $task); - $this->addTags($result, $task, $tags[$task->id]); + $this->addTags($result, $task, $tags[$task->id] ?? []); } return $result; diff --git a/database/migrations/2020_08_18_085114_create_tags_table.php b/database/migrations/2020_08_18_085114_create_tags_table.php index 39a8e9b..6c00987 100644 --- a/database/migrations/2020_08_18_085114_create_tags_table.php +++ b/database/migrations/2020_08_18_085114_create_tags_table.php @@ -23,8 +23,10 @@ class CreateTagsTable extends Migration }); Schema::create('tag_task', function (Blueprint $table) { + $table->id(); $table->unsignedBigInteger('tag_id'); $table->unsignedBigInteger('task_id'); + $table->timestamps(); }); } From 344e13985d023bd13392721e18d18c8a86d8d245 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 16:55:40 +0330 Subject: [PATCH 20/51] complete getValueOf method --- app/Models/Workflow.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Models/Workflow.php b/app/Models/Workflow.php index ecca6b9..3989836 100644 --- a/app/Models/Workflow.php +++ b/app/Models/Workflow.php @@ -38,6 +38,8 @@ class Workflow extends Model 'status_id' => null, 'system_id' => null, 'user_id' => null, + 'task_id' => null, + 'subject_id' => $this->id, ]; if ($key && isset($values, $key)) { From 9c22aa2b12754babd765c10902c9f5f5b65f704b Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 17:10:10 +0330 Subject: [PATCH 21/51] Fix missing vars in method definition --- app/Http/Controllers/ProjectController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 4a396ef..00a20cb 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -22,7 +22,7 @@ class ProjectController extends Controller return Business::info($request->route('business'), true); } - public function update(Request $request,string $project) + public function update(Request $request, int $business, string $project) { permit('projectEdit', ['project_id' => $project]); $project = Project::findOrFail($project); @@ -31,7 +31,7 @@ class ProjectController extends Controller } - public function delete(Request $request, string $project) + public function delete(Request $request, int $business, string $project) { permit('businessProjects'); $project = Project::findOrFail($project); @@ -39,7 +39,7 @@ class ProjectController extends Controller return Business::info($request->route('business')); } - public function restore(Request $request, string $project) + public function restore(Request $request, int $business, string $project) { $project = Project::onlyTrashed()->findOrFail($project); $project->restore(); @@ -139,7 +139,7 @@ class ProjectController extends Controller } } - public function setAvatar(Request $request, string $project) + public function setAvatar(Request $request, int $business, string $project) { $project = Project::findOrFail($project); if ($request->hasFile('avatar')) { @@ -149,7 +149,7 @@ class ProjectController extends Controller return $project; } - public function unSetAvatar(Request $request, string $project) + public function unSetAvatar(Request $request,int $business ,string $project) { $project = Project::findOrFail($project); $project->deleteAvatar(); From 6c868508eea6ec38273ed786301ba8c73bd16d26 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 17:47:26 +0330 Subject: [PATCH 22/51] Fix zarinpal --- config/app.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/app.php b/config/app.php index cc438b2..7f44ebe 100644 --- a/config/app.php +++ b/config/app.php @@ -64,6 +64,7 @@ return [ App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, + App\Utilities\Zarinpal\Laravel\ZarinpalServiceProvider::class, ], 'aliases' => [ @@ -104,5 +105,7 @@ return [ 'URL' => Illuminate\Support\Facades\URL::class, 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, + + 'View' => App\Utilities\Zarinpal\Laravel\Facade\Zarinpal::class, ], ]; From 2c0e162bf40a2b85ea3c8510bcb2444cf16e50ce Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 18:16:51 +0330 Subject: [PATCH 23/51] Fix task file --- app/Http/Controllers/TaskFileController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/TaskFileController.php b/app/Http/Controllers/TaskFileController.php index 6750715..b8bd77c 100644 --- a/app/Http/Controllers/TaskFileController.php +++ b/app/Http/Controllers/TaskFileController.php @@ -11,7 +11,6 @@ use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Http\Resources\FileResource; use Symfony\Component\HttpFoundation\Response; -use Illuminate\Http\Exceptions\HttpResponseException; class TaskFileController extends Controller { @@ -41,7 +40,7 @@ class TaskFileController extends Controller // guest or de active // return files as file resource [$business, $project, $task] = $this->checkBelonging($business, $project, $task); - return FileResource::collection($task->files); + return FileResource::collection($task->files ?? []); } public function sync(Request $request,int $business, int $project, int $task) @@ -78,7 +77,7 @@ class TaskFileController extends Controller // return the file resource or stream it [$business, $project, $task] = $this->checkBelonging($business, $project, $task); - $file = File::find($file); + $file = File::findOrFail($file); if ($file->user_id !== Auth::id()) { abort(Response::HTTP_UNAUTHORIZED); } From fce7fcc652982d622cc1eb7605bca67368092511 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 18:20:19 +0330 Subject: [PATCH 24/51] some minor change in auth and related configs --- app/Http/Controllers/AuthController.php | 5 +++-- config/services.php | 2 +- routes/api.php | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 697f930..7e9a193 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -189,7 +189,8 @@ class AuthController extends Controller */ public function logout(Request $request) { - $token = $request->getCurrentToken(); + $token = $request->bearerToken(); + if (blank($token)) { return new JsonResponse([ 'message' => 'Not authorized request.', @@ -216,7 +217,7 @@ class AuthController extends Controller public function revoke(string $token) { /** @var Fingerprint $token */ - $token = Auth::user()->fingerprints()->firstWhere([ + $token = Fingerprint::firstWhere([ 'token' => $token, ]); diff --git a/config/services.php b/config/services.php index 95b9b0a..5c0ae34 100644 --- a/config/services.php +++ b/config/services.php @@ -16,6 +16,6 @@ return [ 'google' => [ 'client_id' => '1002439248397-oa6hnh25n6qri3q4kst62gvb1k9ki65l.apps.googleusercontent.com', 'client_secret' => 'tKbiyh5hOjYIcj-W1y3N8X5R', - 'redirect' => 'http://localhost:8000/user/v1/auth/google/callback', + 'redirect' => env('APP_URL').'/user/v1/auth/google/callback', ], ]; diff --git a/routes/api.php b/routes/api.php index 7e8d9c9..a51789c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -14,11 +14,11 @@ $router->get('/callback', 'CreditController@callback'); $router->get('/{transaction}/redirection', 'CreditController@redirection'); $router->group(['prefix' => 'auth'], function () use ($router) { - $router->get('/', 'AuthController@auth'); + $router->get('/', 'AuthController@auth')->middleware('auth:api'); $router->delete('/', 'AuthController@delete'); $router->get('/info', 'AuthController@authWithInfo'); $router->post('login', 'AuthController@login'); - $router->post('logout', 'AuthController@logout'); + $router->post('logout', 'AuthController@logout')->middleware('auth:api'); $router->post('register', 'AuthController@register'); $router->post('revoke/{token}', 'AuthController@revoke'); @@ -27,8 +27,8 @@ $router->group(['prefix' => 'auth'], function () use ($router) { $router->post('verification', 'AuthController@verification'); - $router->get('google/redirect', 'AuthController@redirectToGoogle'); - $router->get('google/callback', 'AuthController@handleGoogleCallback'); + $router->get('google/redirect', 'AuthController@redirectToGoogle')->name('google.redirect'); + $router->get('google/callback', 'AuthController@handleGoogleCallback')->name('google.callback'); }); $router->group(['prefix' => 'businesses', 'middleware' => 'auth:api'], function () use ($router) { From 2e8f2d67775f78789038befc423e4928888d5db6 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Wed, 3 Mar 2021 18:45:30 +0330 Subject: [PATCH 25/51] some minor change in user during test --- app/Http/Controllers/UserController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index ceded14..b8a4135 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -63,6 +63,7 @@ class UserController extends Controller { $user = User::findOrFail($user); $user->deleteAvatar(); + $user->refresh(); return $user; } From fc1bd902c4cf3c019a4129a11697efd1686717d8 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Wed, 3 Mar 2021 18:45:54 +0330 Subject: [PATCH 26/51] Fix max bound --- app/Http/Controllers/FileController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index 63816f6..a132732 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers; use App\Models\File; use App\Models\Project; -use App\Rules\maxBound; +use App\Rules\MaxBound; use App\Models\Business; use Illuminate\Support\Str; use Illuminate\Http\Request; @@ -102,7 +102,7 @@ class FileController extends Controller $business = Business::findOrFail($business); $project = Project::findOrFail($project); - // $this->validate($request, ['file' => 'required|file',]); + $this->validate($request, ['file' => 'required|file',]); $file = $request->file('file'); $file_extension = $file->getClientOriginalExtension(); From 215ac23d362ef5064c94860fa1764bde8892c035 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 11:49:43 +0330 Subject: [PATCH 27/51] add middleware --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index a51789c..4a40912 100644 --- a/routes/api.php +++ b/routes/api.php @@ -16,7 +16,7 @@ $router->get('/{transaction}/redirection', 'CreditController@redirection'); $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth')->middleware('auth:api'); $router->delete('/', 'AuthController@delete'); - $router->get('/info', 'AuthController@authWithInfo'); + $router->get('/info', 'AuthController@authWithInfo')->middleware('auth:api'); $router->post('login', 'AuthController@login'); $router->post('logout', 'AuthController@logout')->middleware('auth:api'); $router->post('register', 'AuthController@register'); From d91bebb9c7b636e6eab80b38781e7ee0a20be8e5 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 11:49:43 +0330 Subject: [PATCH 28/51] add middleware --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index a51789c..4a40912 100644 --- a/routes/api.php +++ b/routes/api.php @@ -16,7 +16,7 @@ $router->get('/{transaction}/redirection', 'CreditController@redirection'); $router->group(['prefix' => 'auth'], function () use ($router) { $router->get('/', 'AuthController@auth')->middleware('auth:api'); $router->delete('/', 'AuthController@delete'); - $router->get('/info', 'AuthController@authWithInfo'); + $router->get('/info', 'AuthController@authWithInfo')->middleware('auth:api'); $router->post('login', 'AuthController@login'); $router->post('logout', 'AuthController@logout')->middleware('auth:api'); $router->post('register', 'AuthController@register'); From 3cf22ba2674629f85c254e4d61ba54b3c1372f70 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 16:21:45 +0330 Subject: [PATCH 29/51] add activity => model, controller, migration, routes also add event and listener for model saved --- app/Events/ModelSaved.php | 38 ++++++ app/Http/Controllers/ActivityController.php | 110 ++++++++++++++++++ app/Listeners/ActivityRegistration.php | 73 ++++++++++++ app/Models/Model.php | 27 +++-- app/Providers/EventServiceProvider.php | 5 + ...1_03_06_085855_create_activities_table.php | 45 +++++++ ..._03_06_114918_create_failed_jobs_table.php | 36 ++++++ routes/api.php | 5 + 8 files changed, 327 insertions(+), 12 deletions(-) create mode 100644 app/Events/ModelSaved.php create mode 100644 app/Http/Controllers/ActivityController.php create mode 100644 app/Listeners/ActivityRegistration.php create mode 100644 database/migrations/2021_03_06_085855_create_activities_table.php create mode 100644 database/migrations/2021_03_06_114918_create_failed_jobs_table.php diff --git a/app/Events/ModelSaved.php b/app/Events/ModelSaved.php new file mode 100644 index 0000000..32a28cc --- /dev/null +++ b/app/Events/ModelSaved.php @@ -0,0 +1,38 @@ +message = $message; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Http/Controllers/ActivityController.php b/app/Http/Controllers/ActivityController.php new file mode 100644 index 0000000..870279c --- /dev/null +++ b/app/Http/Controllers/ActivityController.php @@ -0,0 +1,110 @@ +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.system_id' => [new MaxBound($bound)] , + 'filter.workflow_id' => [new MaxBound($bound)] , + 'filter.status_id' => [new MaxBound($bound)] , + 'filter.sprint_id' => [new MaxBound($bound)] , + 'filter.actor_id' => [new MaxBound($bound)] , + 'filter.user_id' => [new MaxBound($bound)] , + 'filter.subject_id' => [new MaxBound($bound)] , + //todo: validation for crud_id and table_id + 'filter.creates_before' => 'bail|nullable|date|date_format:Y-m-d' , + 'filter.creates_after' => 'bail|nullable|date|date_format:Y-m-d' , + 'filter.creates_in' => 'bail|nullable|numeric|max:90' , + ]); + } + public function indexFiltering($business) + { + $query = Activity::where('business_id', $business); + $activityQ = QueryBuilder::for($query) + ->allowedFilters([ + AllowedFilter::exact('project_id'), + AllowedFilter::exact('system_id'), + AllowedFilter::exact('workflow_id'), + AllowedFilter::exact('status_id'), + AllowedFilter::exact('sprint_id'), + AllowedFilter::exact('task_id'), + AllowedFilter::exact('actor_id'), + AllowedFilter::exact('user_id'), + AllowedFilter::exact('crud_id'), + AllowedFilter::exact('table_id'), + AllowedFilter::exact('subject_id'), + AllowedFilter::scope('creates_before'), + AllowedFilter::scope('creates_after'), + AllowedFilter::scope('creates_in'), + ]) + ->defaultSort('-id') + ->allowedSorts('id', 'created_at'); + 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); + $activityQ->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()); + }); + }); + } + return $activityQ; + } + + 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($business, Request $request) + { + return Activity::create($request->merge(['business_id' => $business])->all()); + } + + public function delete() + { + + } +} diff --git a/app/Listeners/ActivityRegistration.php b/app/Listeners/ActivityRegistration.php new file mode 100644 index 0000000..90b97b8 --- /dev/null +++ b/app/Listeners/ActivityRegistration.php @@ -0,0 +1,73 @@ +message)); + $message = json_decode($event->message); + Activity::create([ + 'business_id' => $message->business, + 'project_id' => $message->project, + 'actor_id' => $message->auth, + 'system_id' => $message->data->system_id, + 'workflow_id' => $message->data->workflow_id, + 'status_id' => $message->data->status_id, + 'sprint_id' => $message->data->sprint_id, + 'task_id' => $message->data->task_id ?? null, + 'subject_id' => $message->data->subject_id ?? null, + 'user_id' => $message->data->user_id, + 'crud_id' => $message->data->crud_id, + 'table_id' => enum('tables.'.$message->data->table_name.'.id'), + 'original' => $message->data->original, + 'diff' => $message->data->diff, + ]); + } +} diff --git a/app/Models/Model.php b/app/Models/Model.php index db30979..05ae626 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -4,6 +4,7 @@ namespace App\Models; use Anik\Amqp\Exchange; use Anik\Amqp\Facades\Amqp; +use App\Events\ModelSaved; use PhpAmqpLib\Wire\AMQPTable; use Anik\Amqp\PublishableMessage; use Illuminate\Http\JsonResponse; @@ -196,19 +197,21 @@ class Model extends EloquentModel $message = new PublishableMessage(json_encode($payload)); - $routers = [ - "activity_exchange" => ["name" => "activity",], - "notif_exchange" => ["name" => "notif",], - "socket_exchange" => ["name" => "socket",], - ]; - - foreach ($routers as $exchange => $properties) { - $message->setProperties(["application_headers" => new AMQPTable($properties)]); - - $message->setExchange(new Exchange($exchange)); + ModelSaved::dispatch(json_encode($payload)); - Amqp::publish($message, ""); - } +// $routers = [ +// "activity_exchange" => ["name" => "activity",], +// "notif_exchange" => ["name" => "notif",], +// "socket_exchange" => ["name" => "socket",], +// ]; +// +// foreach ($routers as $exchange => $properties) { +// $message->setProperties(["application_headers" => new AMQPTable($properties)]); +// +// $message->setExchange(new Exchange($exchange)); +// +// Amqp::publish($message, ""); +// } } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a9f10a6..4d702ef 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use App\Events\ModelSaved; +use App\Listeners\ActivityRegistration; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -18,6 +20,9 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ SendEmailVerificationNotification::class, ], + ModelSaved::class => [ + ActivityRegistration::class + ] ]; /** diff --git a/database/migrations/2021_03_06_085855_create_activities_table.php b/database/migrations/2021_03_06_085855_create_activities_table.php new file mode 100644 index 0000000..ee00afe --- /dev/null +++ b/database/migrations/2021_03_06_085855_create_activities_table.php @@ -0,0 +1,45 @@ +id(); + $table->unsignedBigInteger('business_id'); + $table->unsignedBigInteger('project_id')->nullable(); + $table->unsignedBigInteger('system_id')->nullable(); + $table->unsignedBigInteger('workflow_id')->nullable(); + $table->unsignedBigInteger('status_id')->nullable(); + $table->unsignedBigInteger('sprint_id')->nullable(); + $table->unsignedBigInteger('task_id')->nullable(); + $table->unsignedBigInteger('subject_id')->nullable();//row id + $table->unsignedBigInteger('actor_id'); + $table->unsignedBigInteger('user_id')->nullable(); + $table->unsignedBigInteger('crud_id')->nullable(); + $table->unsignedBigInteger('table_id')->nullable(); + $table->json('original')->nullable(); // a unique identifier that represent the type of action + $table->json('diff')->nullable(); // all data that has been changed + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('activities'); + } +} diff --git a/database/migrations/2021_03_06_114918_create_failed_jobs_table.php b/database/migrations/2021_03_06_114918_create_failed_jobs_table.php new file mode 100644 index 0000000..6aa6d74 --- /dev/null +++ b/database/migrations/2021_03_06_114918_create_failed_jobs_table.php @@ -0,0 +1,36 @@ +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'); + } +} diff --git a/routes/api.php b/routes/api.php index 4a40912..be7bd04 100644 --- a/routes/api.php +++ b/routes/api.php @@ -182,6 +182,11 @@ $router->group(['prefix' => 'businesses', 'middleware' => 'auth:api'], function $router->delete('/', 'BusinessController@deleteUser'); }); }); + + $router->group(['prefix' => 'activities'], function () use ($router) { + $router->post('/', 'ActivityController@store'); + $router->get('/', 'ActivityController@index'); + }); }); }); From 42ff4828866fa4133b3ca522876067ec059b35d0 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 16:22:03 +0330 Subject: [PATCH 30/51] add model --- app/Models/Activity.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 805e4a8..ed20699 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -2,8 +2,37 @@ namespace App\Models; -use App\Models\Model; +use Carbon\Carbon; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; + class Activity extends Model { + use HasFactory; + + protected $fillable = [ + 'business_id', 'project_id', 'system_id', 'workflow_id', 'status_id', 'sprint_id', + 'actor_id', 'task_id', 'subject_id', 'user_id', 'crud_id', 'table_id', 'original', 'diff' + ]; + + public $casts = [ + 'original' => 'array', + 'diff' => 'array', + ]; + + public function scopeCreatesBefore($query, $date) + { + return $query->whereDate('created_at', '<=', Carbon::parse($date)); + } + public function scopeCreatesAfter($query, $date) + { + return $query->whereDate('created_at', '>=', Carbon::parse($date)); + } + public function scopeCreatesIn($query, $days) + { + return $days != "" ? + $query->whereDate('created_at', '>=', Carbon::now()->modify('-'.$days.' day')->toDate()) : + $query; + } } From f6afd0a79ea9ca0605685c6fc37372bf5bfb905e Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 17:56:51 +0330 Subject: [PATCH 31/51] fix estimated time --- app/Models/Task.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Task.php b/app/Models/Task.php index ffef4f7..aee207b 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -73,7 +73,7 @@ class Task extends Model 'title' => 'required|string|min:3|max:254', 'description' => 'nullable|string|min:2|max:1000', 'priority' => 'nullable|numeric|between:1,10', - 'estimated_time' => 'nullable|numeric|min:30', + 'estimated_time' => 'bail|nullable|numeric', 'due_date' => 'bail|nullable|date|date_format:Y-m-d|after_or_equal:'. ((request()->method() === 'POST') ? date('yy-m-d') : $this->created_at->toDateString()), 'tags' => 'nullable|array', From ce7b2e2aa433b5424c535a88c91588546232c043 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 18:02:53 +0330 Subject: [PATCH 32/51] fix tags --- app/Http/Resources/TaskResource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Resources/TaskResource.php b/app/Http/Resources/TaskResource.php index eab56f3..94491fb 100644 --- a/app/Http/Resources/TaskResource.php +++ b/app/Http/Resources/TaskResource.php @@ -21,7 +21,7 @@ class TaskResource extends JsonResource } } - $resource['tags'] = $this->tags; + $resource['tags'] = $this->tags()->pluck('tag_id')->toArray(); $resource['works'] = $this->works; $resource['comments'] = $this->comments; From a02ec57ab0f3b499af778fcccf3dbc36482a4b76 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 18:28:57 +0330 Subject: [PATCH 33/51] change .env.example --- .env.example | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index d1f8a6a..fde9c84 100644 --- a/.env.example +++ b/.env.example @@ -25,8 +25,9 @@ SESSION_LIFETIME=120 MEMCACHED_HOST=127.0.0.1 -REDIS_HOST=127.0.0.1 -REDIS_PASSWORD=null +REDIS_HOST=redis +REDIS_PASSWORD=root +REDIS_USER=root REDIS_PORT=6379 MAIL_MAILER=smtp From 5fb0a4335ba25d7ca786e27b23c0f0aef8ea20c5 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Sat, 6 Mar 2021 18:57:30 +0330 Subject: [PATCH 34/51] Bug fix and improvments of cost work --- app/Console/Commands/CostCommand.php | 203 +++++++++++------- config/app.php | 2 +- database/factories/BusinessFactory.php | 2 +- ...0_08_18_085018_create_businesses_table.php | 2 +- 4 files changed, 123 insertions(+), 86 deletions(-) diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php index 99a9db5..9845050 100644 --- a/app/Console/Commands/CostCommand.php +++ b/app/Console/Commands/CostCommand.php @@ -2,9 +2,12 @@ namespace App\Console\Commands; -use DB; +use Throwable; +use Carbon\Carbon; use App\Models\Business; use Illuminate\Console\Command; +use Morilog\Jalali\CalendarUtils; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Cache; class CostCommand extends Command @@ -28,109 +31,143 @@ class CostCommand extends Command 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); + $business = Business::find(221); + if ($business === null) { + continue; + } + + $year = jdate()->getYear(); + $month = jdate()->getMonth(); + $day = jdate()->getDay(); + $hour = jdate()->getHour(); + $minute = jdate()->getMinute(); + $second = jdate()->getSecond(); + + if (jdate()->getYear() > jdate($business->calculated_at)->getYear()) { + $year = jdate()->getYear(); + $month = 1; + $day = 1; } + if ( + jdate()->getYear() > jdate($business->calculated_at)->getYear() + && + jdate()->getMonth() > jdate($business->calculated_at)->getMonth() + ) { + $year = jdate()->getYear(); + $month = jdate()->getMonth(); + $day = 1; + } + + $gDate = CalendarUtils::toGregorian($year, $month, $day); + $carbon = Carbon::createFromDate($gDate[0], $gDate[1], $gDate[2]); + $carbon->setTime($hour, $minute, $second); + + $now = $carbon; + // if calculated_at less than an hour stop - if (\Carbon\Carbon::now()->diffInMinutes($until_now) <= 60) { - $this->info('nothing to cost'); + if ($now->diffInMinutes($business->calculated_at)) { + $this->info('Must be one hour after the last audit.'); continue; } try { DB::beginTransaction(); + // Fixed amounts of expenses + $business->load('users', 'cost'); - // 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; + $costs = 0; + $costs += $this->calculateCostOfBusinessUsers($business, $now); + $costs += $this->calculateCostOfBusinessFiles($business, $now); // 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(), + 'calculated_at' => Carbon::now(), ]); + DB::commit(); - } catch (Throwable $thr) { + } catch (Throwable $throwable) { DB::rollback(); - throw $thr; + report($throwable); continue; } } } + + public function calculateCostOfBusinessUsers($business, $until_now) + { + $user_fee = enum('business.fee.user'); + $calculated_at = $business->calculated_at; + $recorded_month = jdate($business->calculated_at)->format("Y-m-01"); + + + // 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) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month), + 'additional' => $business->users->pluck('id')->toArray(), + ]); + } + + return $user_fee * $duration; + } + + public function calculateCostOfBusinessFiles($business, $until_now) + { + $file_fee = enum('business.fee.file'); + $calculated_at = $business->calculated_at; + $recorded_month = jdate($business->calculated_at)->format("Y-m-01"); + + + // do the math in php + if (intdiv($business->files_volume, 200) === 0) { + $pads = 0; + } else { + $pads = intdiv($business->files_volume, 200) - 1; + } + + + $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) + $files->duration, // last calc - (current month - now else last calc - end of the past month),, + ]); + } + + return $file_fee * $duration; + } } diff --git a/config/app.php b/config/app.php index 7f44ebe..d517cdc 100644 --- a/config/app.php +++ b/config/app.php @@ -12,7 +12,7 @@ return [ 'asset_url' => env('ASSET_URL', null), - 'timezone' => 'UTC', + 'timezone' => 'Asia/Tehran', 'locale' => 'fa', diff --git a/database/factories/BusinessFactory.php b/database/factories/BusinessFactory.php index f8dab3d..28f0dae 100644 --- a/database/factories/BusinessFactory.php +++ b/database/factories/BusinessFactory.php @@ -12,6 +12,6 @@ $factory->define(Business::class, function (Faker $faker) { 'slug' => Str::slug($name) . $faker->numberBetween(1, 100), 'wallet' => random_int(111111, 999999), 'color' => $faker->colorName, - 'calculated_at' => \Carbon\Carbon::now()->subMinutes(random_int(1, 1000)), + 'calculated_at' => \Carbon\Carbon::now()->subDays(random_int(1, 31)), ]; }); diff --git a/database/migrations/2020_08_18_085018_create_businesses_table.php b/database/migrations/2020_08_18_085018_create_businesses_table.php index e37d713..b0d168f 100644 --- a/database/migrations/2020_08_18_085018_create_businesses_table.php +++ b/database/migrations/2020_08_18_085018_create_businesses_table.php @@ -23,7 +23,7 @@ class CreateBusinessesTable extends Migration $table->string('description')->nullable(); $table->json('cache')->nullable(); $table->boolean('has_avatar')->default(false); - $table->timestamp('calculated_at')->nullable(); + $table->timestamp('calculated_at')->nullable()->index(); $table->timestamp('created_at')->nullable(); $table->timestamp('updated_at')->nullable(); $table->timestamp('deleted_at')->nullable(); From 882db1170eaf669a40a4da5c233b63de70b9cab9 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sat, 6 Mar 2021 18:58:31 +0330 Subject: [PATCH 35/51] minor change in model --- app/Models/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Model.php b/app/Models/Model.php index 05ae626..c68422f 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -195,7 +195,7 @@ class Model extends EloquentModel 'from' => env('CONTAINER_NAME'), ]; - $message = new PublishableMessage(json_encode($payload)); +// $message = new PublishableMessage(json_encode($payload)); ModelSaved::dispatch(json_encode($payload)); From c2f9518b9aa21516856ae7531a38bef840137483 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Sun, 7 Mar 2021 15:43:41 +0330 Subject: [PATCH 36/51] Bug fix in fingerprints --- database/migrations/2020_08_18_085017_fingerprints.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/migrations/2020_08_18_085017_fingerprints.php b/database/migrations/2020_08_18_085017_fingerprints.php index 67bd1ee..af506ec 100644 --- a/database/migrations/2020_08_18_085017_fingerprints.php +++ b/database/migrations/2020_08_18_085017_fingerprints.php @@ -19,8 +19,8 @@ class Fingerprints extends Migration $table->string('agent'); $table->ipAddress('ip'); $table->string('os'); - $table->decimal('latitude', 10, 2); - $table->decimal('longitude', 11, 2); + $table->decimal('latitude', 10, 4); + $table->decimal('longitude', 11, 4); $table->char('token', 60)->unique(); $table->timestamps(); }); From c89ea8683854c83a493e4ecd34beea8f7c435b20 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Sun, 7 Mar 2021 17:24:41 +0330 Subject: [PATCH 37/51] Remove redunant packages --- composer.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composer.json b/composer.json index ca1dcad..a8d0129 100644 --- a/composer.json +++ b/composer.json @@ -20,11 +20,6 @@ "jenssegers/agent": "^2.6", "laravel/socialite": "^5.1", "laravel/framework": "^8.12", - "guzzlehttp/guzzle": "^7.0.1", - "laravel/legacy-factories": "^1", - "fruitcake/laravel-cors": "^2.0", - "laravel/lumen-framework": "^8.0", - "illuminate/notifications": "^8.0", "league/flysystem-aws-s3-v3": "~1.0", "spatie/laravel-medialibrary": "^9.0", "spatie/laravel-query-builder": "^3.3", From c9bf70b9dc29a1d4379b509d6501ce89b25d70fa Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Sun, 7 Mar 2021 17:34:29 +0330 Subject: [PATCH 38/51] Fix a few problem after removing lumen --- app/Http/Controllers/AuthController.php | 1 - app/Models/Model.php | 4 ---- app/Models/User.php | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 7e9a193..55fe6f7 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -13,7 +13,6 @@ 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; diff --git a/app/Models/Model.php b/app/Models/Model.php index c68422f..b2f1373 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -2,11 +2,7 @@ namespace App\Models; -use Anik\Amqp\Exchange; -use Anik\Amqp\Facades\Amqp; use App\Events\ModelSaved; -use PhpAmqpLib\Wire\AMQPTable; -use Anik\Amqp\PublishableMessage; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Auth; diff --git a/app/Models/User.php b/app/Models/User.php index 857c7d3..72c3b64 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,13 +5,13 @@ namespace App\Models; use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; -use App\Models\ReportableRelation; use Illuminate\Validation\Rule; use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; +use App\Models\ReportableRelation; use Illuminate\Auth\Authenticatable; -use Laravel\Lumen\Auth\Authorizable; use Spatie\MediaLibrary\InteractsWithMedia; +use Illuminate\Foundation\Auth\Access\Authorizable; use Spatie\MediaLibrary\MediaCollections\Models\Media; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; From 9191e7705e486d66c618bcd2a894ff760f643c46 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sun, 7 Mar 2021 19:42:00 +0330 Subject: [PATCH 39/51] add event listener, notification and fa validations some change in enums --- app/Enums/cruds.php | 11 ++ app/Enums/tables.php | 51 +++++-- app/Events/TagCreate.php | 38 +++++ app/Listeners/NotifHandler.php | 36 +++++ app/Listeners/TagCreateNotif.php | 37 +++++ app/Models/User.php | 4 +- app/Notifications/MailNotification.php | 87 ++++++++++++ app/Providers/EventServiceProvider.php | 9 +- config/mail.php | 4 +- docker-compose.yml | 9 ++ resources/lang/fa/auth.php | 19 +++ resources/lang/fa/notification.php | 20 +++ resources/lang/fa/pagination.php | 19 +++ resources/lang/fa/passwords.php | 22 +++ resources/lang/fa/validation.php | 189 +++++++++++++++++++++++++ 15 files changed, 539 insertions(+), 16 deletions(-) create mode 100644 app/Enums/cruds.php create mode 100644 app/Events/TagCreate.php create mode 100644 app/Listeners/NotifHandler.php create mode 100644 app/Listeners/TagCreateNotif.php create mode 100644 app/Notifications/MailNotification.php create mode 100644 resources/lang/fa/auth.php create mode 100644 resources/lang/fa/notification.php create mode 100644 resources/lang/fa/pagination.php create mode 100644 resources/lang/fa/passwords.php create mode 100644 resources/lang/fa/validation.php diff --git a/app/Enums/cruds.php b/app/Enums/cruds.php new file mode 100644 index 0000000..1980e2e --- /dev/null +++ b/app/Enums/cruds.php @@ -0,0 +1,11 @@ + [ + 10 => ['name' => 'Create'], + 20 => ['name' => 'Update'], + 30 => ['name' => 'Delete'], + ], + +]; diff --git a/app/Enums/tables.php b/app/Enums/tables.php index 95a3d03..273bf4c 100644 --- a/app/Enums/tables.php +++ b/app/Enums/tables.php @@ -2,55 +2,82 @@ return [ + 'inverse' => [ + 10 => ['name' => 'Businesses'], + 20 => ['name' => 'Projects'], + 30 => ['name' => 'Sprints'], + 40 => ['name' => 'Statuses'], + 50 => ['name' => 'Systems'], + 60 => ['name' => 'Workflows'], + 70 => ['name' => 'Tags'], + 80 => ['name' => 'Tasks'], + 90 => ['name' => 'Works'], + 210 => ['name' => 'BusinessUser'], + 220 => ['name' => 'ProjectUser'], + 230 => ['name' => 'TagTask'], + ], + //Main Table's 'businesses' => [ 'id' => 10, - 'name' => 'Businesses' + 'name' => 'Businesses', + 'singular_name' => 'Business', ], 'projects' => [ 'id' => 20, - 'name' => 'Projects' + 'name' => 'Projects', + 'singular_name' => 'Project', ], 'sprints' => [ 'id' => 30, - 'name' => 'Sprints' + 'name' => 'Sprints', + 'singular_name' => 'Sprint', ], 'statuses' => [ 'id' => 40, - 'name' => 'Statuses' + 'name' => 'Statuses', + 'singular_name' => 'Status', ], 'systems' => [ 'id' => 50, - 'name' => 'Systems' + 'name' => 'Systems', + 'singular_name' => 'System', ], 'workflows' => [ 'id' => 60, - 'name' => 'Workflows' + 'name' => 'Workflows', + 'singular_name' => 'Workflow', ], 'tags' => [ 'id' => 70, - 'name' => 'Tags' + 'name' => 'Tags', + 'singular_name' => 'Tag', ], 'tasks' => [ 'id' => 80, - 'name' => 'Tasks' + 'name' => 'Tasks', + 'singular_name' => 'Task', ], 'works' => [ 'id' => 90, - 'name' => 'Works' + 'name' => 'Works', + 'singular_name' => 'Work', ], //Relation Table's 'business_user' => [ 'id' => 210, - 'name' => 'BusinessUser' + 'name' => 'BusinessUser', + 'singular_name' => 'BusinessUser', ], 'project_user' => [ 'id' => 220, - 'name' => 'ProjectUser' + 'name' => 'ProjectUser', + 'singular_name' => 'ProjectUser', ], 'tag_task' => [ 'id' => 230, - 'name' => 'TagTask' + 'name' => 'TagTask', + 'singular_name' => 'TagTask', ], ]; diff --git a/app/Events/TagCreate.php b/app/Events/TagCreate.php new file mode 100644 index 0000000..0a86866 --- /dev/null +++ b/app/Events/TagCreate.php @@ -0,0 +1,38 @@ +message = $message; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Listeners/NotifHandler.php b/app/Listeners/NotifHandler.php new file mode 100644 index 0000000..1fed4db --- /dev/null +++ b/app/Listeners/NotifHandler.php @@ -0,0 +1,36 @@ +message); + $event_class = 'App\Events\\'.enum('tables.'.$message->data->table_name.'.singular_name').enum('cruds.inverse.'.$message->data->crud_id.'.name'); + if (class_exists($event_class)) { +// event(new ('App\Events\\'.$event_class($message))); + $event_class::dispatch($message); + } + } +} diff --git a/app/Listeners/TagCreateNotif.php b/app/Listeners/TagCreateNotif.php new file mode 100644 index 0000000..8222723 --- /dev/null +++ b/app/Listeners/TagCreateNotif.php @@ -0,0 +1,37 @@ +message; + $notif_line = 'tags.create'; + $users = User::where('id', '<', 10)->get(); + Notification::send($users, new MailNotification($notif_line)); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 857c7d3..b1549d3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ use App\Models\File; use App\Models\Model; use App\Models\SoftDeletes; use App\Models\ReportableRelation; +use Illuminate\Notifications\Notifiable; use Illuminate\Validation\Rule; use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; @@ -18,7 +19,8 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia { - use SoftDeletes, + use Notifiable, + SoftDeletes, Authorizable, Authenticatable, InteractsWithMedia; diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php new file mode 100644 index 0000000..4122952 --- /dev/null +++ b/app/Notifications/MailNotification.php @@ -0,0 +1,87 @@ +message = $message; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line(__('notification.'.$this->message)) + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 4d702ef..c627395 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,7 +3,10 @@ namespace App\Providers; use App\Events\ModelSaved; +use App\Events\TagCreate; use App\Listeners\ActivityRegistration; +use App\Listeners\NotifHandler; +use App\Listeners\TagCreateNotif; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -21,7 +24,11 @@ class EventServiceProvider extends ServiceProvider SendEmailVerificationNotification::class, ], ModelSaved::class => [ - ActivityRegistration::class + ActivityRegistration::class, + NotifHandler::class, + ], + TagCreate::class => [ + TagCreateNotif::class, ] ]; diff --git a/config/mail.php b/config/mail.php index 54299aa..022418f 100644 --- a/config/mail.php +++ b/config/mail.php @@ -36,8 +36,8 @@ return [ 'mailers' => [ 'smtp' => [ 'transport' => 'smtp', - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), + 'host' => env('MAIL_HOST', 'localhost'), + 'port' => env('MAIL_PORT', 1025), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), diff --git a/docker-compose.yml b/docker-compose.yml index 96ea2ee..d606ce7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,6 +100,15 @@ services: - mysql networks: - sail + mailhog: + image: mailhog/mailhog + logging: + driver: 'none' # disable saving logs + ports: + - 1025:1025 # smtp server + - 8025:8025 # web ui + networks: + - sail networks: sail: driver: bridge diff --git a/resources/lang/fa/auth.php b/resources/lang/fa/auth.php new file mode 100644 index 0000000..a5831f3 --- /dev/null +++ b/resources/lang/fa/auth.php @@ -0,0 +1,19 @@ + 'اطلاعات وارد شده صحیح نمی باشد', + 'throttle' => 'درخواست بیش از حد مجاز! لطفا بعد از :seconds ثانیه دوباره امتحان کنید', + +]; diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php new file mode 100644 index 0000000..e885bcc --- /dev/null +++ b/resources/lang/fa/notification.php @@ -0,0 +1,20 @@ + [ + 'create' => 'یک تگ ساخته شد.' + ] + +]; diff --git a/resources/lang/fa/pagination.php b/resources/lang/fa/pagination.php new file mode 100644 index 0000000..e00adbf --- /dev/null +++ b/resources/lang/fa/pagination.php @@ -0,0 +1,19 @@ + '« قبلی', + 'next' => 'بعدی »', + +]; diff --git a/resources/lang/fa/passwords.php b/resources/lang/fa/passwords.php new file mode 100644 index 0000000..671b232 --- /dev/null +++ b/resources/lang/fa/passwords.php @@ -0,0 +1,22 @@ + 'رمز عبور شما با موفقیت بازیابی شد', + 'sent' => 'ایمیلی برای بازیابی رمزعبور برای شما ارسال شد', + 'throttled' => 'لطفا اندکی صبر کنید', + 'token' => 'مشخصه بازیابی رمزعبور شما صحیح نمی باشد', + 'user' => "کاربری با این اطلاعات وجود ندارد", + +]; diff --git a/resources/lang/fa/validation.php b/resources/lang/fa/validation.php new file mode 100644 index 0000000..54b4e77 --- /dev/null +++ b/resources/lang/fa/validation.php @@ -0,0 +1,189 @@ + 'گزینه :attribute باید تایید شود', + 'active_url' => 'گزینه :attribute یک آدرس سایت معتبر نیست', + 'after' => 'گزینه :attribute باید تاریخی بعد از :date باشد', + 'after_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا بعد از :date باشد', + 'alpha' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'alpha_dash' => 'گزینه :attribute باید تنها شامل حروف، اعداد، خط تیره و زیر خط باشد', + 'alpha_num' => 'گزینه :attribute باید تنها شامل حروف و اعداد باشد', + 'array' => 'گزینه :attribute باید آرایه باشد', + 'before' => 'گزینه :attribute باید تاریخی قبل از :date باشد', + 'before_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا قبل از :date باشد', + 'between' => [ + 'numeric' => 'گزینه :attribute باید بین :min و :max باشد', + 'file' => 'گزینه :attribute باید بین :min و :max کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بین :min و :max کاراکتر باشد', + 'array' => 'گزینه :attribute باید بین :min و :max آیتم باشد', + ], + 'boolean' => 'گزینه :attribute تنها می تواند صحیح یا غلط باشد', + 'confirmed' => 'تایید مجدد گزینه :attribute صحیح نمی باشد', + 'date' => 'گزینه :attribute یک تاریخ صحیح نمی باشد', + 'date_equals' => 'گزینه :attribute باید تاریخی مساوی با :date باشد', + 'date_format' => 'گزینه :attribute با فرمت :format همخوانی ندارد', + 'different' => 'گزینه :attribute و :other باید متفاوت باشند', + 'digits' => 'گزینه :attribute باید :digits عدد باشد', + 'digits_between' => 'گزینه :attribute باید بین :min و :max عدد باشد', + 'dimensions' => 'ابعاد تصویر گزینه :attribute مجاز نمی باشد', + 'distinct' => 'گزینه :attribute دارای افزونگی داده می باشد', + 'email' => 'گزینه :attribute باید یک آدرس ایمیل صحیح باشد', + 'ends_with' => 'گزینه :attribute باید با یکی از این مقادیر پایان یابد، :values', + 'exists' => 'گزینه انتخاب شده :attribute صحیح نمی باشد', + 'file' => 'گزینه :attribute باید یک فایل باشد', + 'filled' => 'گزینه :attribute نمی تواند خالی باشد', + 'gt' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر از :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید بیشتر از :value آیتم باشد', + ], + 'gte' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر یا مساوی :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر یا مساوی :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر یا مساوی :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید :value آیتم یا بیشتر داشته باشد', + ], + 'image' => 'گزینه :attribute باید از نوع تصویر باشد', + 'in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'in_array' => 'گزینه :attribute در :other وجود ندارد', + 'integer' => 'گزینه :attribute باید از نوع عددی باشد', + 'ip' => 'گزینه :attribute باید آی پی آدرس باشد', + 'ipv4' => 'گزینه :attribute باید آی پی آدرس ورژن 4 باشد', + 'ipv6' => 'گزینه :attribute باید آی پی آدرس ورژن 6 باشد', + 'json' => 'گزینه :attribute باید از نوع رشته جیسون باشد', + 'lt' => [ + 'numeric' => 'گزینه :attribute باید کمتر از :value باشد', + 'file' => 'گزینه :attribute باید کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید کمتر از :value آیتم داشته باشد', + ], + 'lte' => [ + 'numeric' => 'گزینه :attribute باید مساوی یا کمتر از :value باشد', + 'file' => 'گزینه :attribute باید مساوی یا کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید مساوی یا کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute نباید کمتر از :value آیتم داشته باشد', + ], + 'max' => [ + 'numeric' => 'گزینه :attribute نباید بزرگتر از :max باشد', + 'file' => 'گزینه :attribute نباید بزرگتر از :max کیلوبایت باشد', + 'string' => 'گزینه :attribute نباید بزرگتر از :max کاراکتر باشد', + 'array' => 'گزینه :attribute نباید بیشتر از :max آیتم داشته باشد', + ], + 'mimes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'mimetypes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'min' => [ + 'numeric' => 'گزینه :attribute باید حداقل :min باشد', + 'file' => 'گزینه :attribute باید حداقل :min کیلوبایت باشد', + 'string' => 'گزینه :attribute باید حداقل :min کاراکتر باشد', + 'array' => 'گزینه :attribute باید حداقل :min آیتم داشته باشد', + ], + 'not_in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'not_regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'numeric' => 'گزینه :attribute باید از نوع عددی باشد', + 'password' => 'رمزعبور صحیح نمی باشد', + 'present' => 'گزینه :attribute باید از نوع درصد باشد', + 'regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'required' => 'تکمیل گزینه :attribute الزامی است', + 'required_if' => 'تکمیل گزینه :attribute زمانی که :other دارای مقدار :value است الزامی می باشد', + 'required_unless' => 'تکمیل گزینه :attribute الزامی می باشد مگر :other دارای مقدار :values باشد', + 'required_with' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد است الزامی است', + 'required_with_all' => 'تکمیل گزینه :attribute زمانی که مقادیر :values درصد است الزامی می باشد', + 'required_without' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد نیست الزامی است', + 'required_without_all' => 'تکمیل گزینه :attribute زمانی که هیچ کدام از مقادیر :values درصد نیست الزامی است', + 'same' => 'گزینه های :attribute و :other باید یکی باشند', + 'size' => [ + 'numeric' => 'گزینه :attribute باید :size باشد', + 'file' => 'گزینه :attribute باید :size کیلوبایت باشد', + 'string' => 'گزینه :attribute باید :size کاراکتر باشد', + 'array' => 'گزینه :attribute باید شامل :size آیتم باشد', + ], + 'starts_with' => 'گزینه :attribute باید با یکی از این مقادیر شروع شود، :values', + 'string' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'timezone' => 'گزینه :attribute باید از نوع منطقه زمانی صحیح باشد', + 'unique' => 'این :attribute از قبل ثبت شده است', + 'uploaded' => 'آپلود گزینه :attribute شکست خورد', + 'url' => 'فرمت :attribute اشتباه است', + 'uuid' => 'گزینه :attribute باید یک UUID صحیح باشد', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [ + 'name' => 'نام', + 'username' => 'نام کاربری', + 'email' => 'ایمیل', + 'first_name' => 'نام', + 'last_name' => 'نام خانوادگی', + 'password' => 'رمز عبور', + 'password_confirmation' => 'تاییدیه رمز عبور', + 'city' => 'شهر', + 'state' => 'استان', + 'country' => 'کشور', + 'address' => 'آدرس', + 'phone' => 'تلفن', + 'mobile' => 'تلفن همراه', + 'age' => 'سن', + 'sex' => 'جنسیت', + 'gender' => 'جنسیت', + 'day' => 'روز', + 'month' => 'ماه', + 'year' => 'سال', + 'hour' => 'ساعت', + 'minute' => 'دقیقه', + 'second' => 'ثانیه', + 'title' => 'عنوان', + 'text' => 'متن', + 'content' => 'محتوا', + 'description' => 'توضیحات', + 'date' => 'تاریخ', + 'time' => 'زمان', + 'available' => 'موجود', + 'type' => 'نوع', + 'img' => 'تصویر', + 'image' => 'تصویر', + 'size' => 'اندازه', + 'color' => 'رنگ', + 'captcha' => 'کد امنیتی', + 'price' => 'قیمت', + 'pic' => 'تصویر', + 'link' => 'لینک', + ], +]; From 34998e384f9a2d996672cee56f911fcec83c3585 Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Mon, 8 Mar 2021 16:34:57 +0330 Subject: [PATCH 40/51] Is there an award for this? Cost worker improvement --- app/Console/Commands/CostCommand.php | 104 +++++++++++------------- app/Http/Controllers/FileController.php | 6 +- app/Http/Resources/FileResource.php | 2 - app/Models/Business.php | 1 + app/Models/Cost.php | 4 + app/Providers/AuthServiceProvider.php | 2 +- composer.json | 2 + 7 files changed, 59 insertions(+), 62 deletions(-) diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php index 9845050..1e05de4 100644 --- a/app/Console/Commands/CostCommand.php +++ b/app/Console/Commands/CostCommand.php @@ -4,78 +4,60 @@ namespace App\Console\Commands; use Throwable; use Carbon\Carbon; +use App\Models\Cost; use App\Models\Business; use Illuminate\Console\Command; use Morilog\Jalali\CalendarUtils; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Storage; 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() { while (true) { - $business = Business::find(221); - if ($business === null) { + $lock = Cache::get('lock', false); + if ($lock) { + $this->info('There is no business for auditing.'); continue; } - $year = jdate()->getYear(); - $month = jdate()->getMonth(); - $day = jdate()->getDay(); - $hour = jdate()->getHour(); - $minute = jdate()->getMinute(); - $second = jdate()->getSecond(); - - if (jdate()->getYear() > jdate($business->calculated_at)->getYear()) { - $year = jdate()->getYear(); - $month = 1; - $day = 1; + $business = Business::orderBy('calculated_at')->first(); + if ($business === null) { + continue; } - if ( - jdate()->getYear() > jdate($business->calculated_at)->getYear() - && - jdate()->getMonth() > jdate($business->calculated_at)->getMonth() - ) { - $year = jdate()->getYear(); - $month = jdate()->getMonth(); - $day = 1; + if ($business->calculated_at->isFuture()) { + continue; } - $gDate = CalendarUtils::toGregorian($year, $month, $day); - $carbon = Carbon::createFromDate($gDate[0], $gDate[1], $gDate[2]); - $carbon->setTime($hour, $minute, $second); + $next_month = jdate($business->calculated_at)->addMonths(); + [$year, $month, $day] = CalendarUtils::toGregorian( + $next_month->getYear(), $next_month->getMonth(), 1 + ); + $now = Carbon::createFromDate($year, $month, $day); + $now->setTime(0, 0, 0); - $now = $carbon; + if ($now->isFuture()) { + $now = Carbon::now(); + } // if calculated_at less than an hour stop - if ($now->diffInMinutes($business->calculated_at)) { + if ($now->diffInMinutes($business->calculated_at) <= 59) { $this->info('Must be one hour after the last audit.'); + Cache::put('lock', true, $now->diffInSeconds($business->calculated_at)); continue; } try { DB::beginTransaction(); // Fixed amounts of expenses - $business->load('users', 'cost'); + $business->load('users', 'files'); $costs = 0; $costs += $this->calculateCostOfBusinessUsers($business, $now); @@ -86,11 +68,13 @@ class CostCommand extends Command // make sure save the calculated_at $business->update([ 'wallet' => $business->wallet - $costs, - 'calculated_at' => Carbon::now(), + 'calculated_at' => $now, ]); DB::commit(); + $this->info("The business #{$business->id} was audited."); } catch (Throwable $throwable) { + throw $throwable; DB::rollback(); report($throwable); continue; @@ -98,16 +82,18 @@ class CostCommand extends Command } } - public function calculateCostOfBusinessUsers($business, $until_now) + public function calculateCostOfBusinessUsers($business, $now) { $user_fee = enum('business.fee.user'); - $calculated_at = $business->calculated_at; $recorded_month = jdate($business->calculated_at)->format("Y-m-01"); + if ($business->users->isEmpty()) { + return 0; + } // get business employee $users_cost = $business->cost - ->where('type', '=', 'users') + ->where('type', '=', Cost::USER_TYPE) ->where('fee', '=', $user_fee) ->where('month', '=', $recorded_month) ->where('amount', '=', $business->users->count()) @@ -115,16 +101,16 @@ class CostCommand extends Command if ($users_cost === null) { $business->cost()->create([ - 'type' => 'users', + 'type' => Cost::USER_TYPE, '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 + 'duration' => $duration = $now->diffInMinutes($business->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) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month), + 'duration' => $duration = $now->diffInMinutes($business->calculated_at) + $users_cost->duration, // last calc - (current month - now else last calc - end of the past month), 'additional' => $business->users->pluck('id')->toArray(), ]); } @@ -132,7 +118,7 @@ class CostCommand extends Command return $user_fee * $duration; } - public function calculateCostOfBusinessFiles($business, $until_now) + public function calculateCostOfBusinessFiles($business, $now) { $file_fee = enum('business.fee.file'); $calculated_at = $business->calculated_at; @@ -140,31 +126,33 @@ class CostCommand extends Command // do the math in php - if (intdiv($business->files_volume, 200) === 0) { - $pads = 0; + $packs = intdiv($business->files_volume, 200); + if ($packs === 0) { + return 0; } else { - $pads = intdiv($business->files_volume, 200) - 1; + $packs--; } - $files = $business->cost - ->where('type', '=', 'files') + ->where('type', '=', Cost::FILE_TYPE) ->where('fee', '=', $file_fee) ->where('month', '=', $recorded_month) - ->where('amount', '=', $business->files_volume) + ->where('amount', '=', $packs) ->first(); if ($files === null) { $business->cost()->create([ - 'type' => 'files', + 'type' => Cost::FILE_TYPE, 'month' => $recorded_month, - 'amount' => $pads, + 'amount' => $packs, 'fee' => $file_fee, - 'duration' => $duration = $until_now->diffInMinutes($calculated_at), // how to determine the file?, + 'duration' => $duration = $now->diffInMinutes($calculated_at), // how to determine the file?, + 'additional' => ['volume' => $business->files_volume], ]); } else { $files->update([ - 'duration' => $duration = $until_now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),, + 'duration' => $duration = $now->diffInMinutes($calculated_at) + $files->duration, // last calc - (current month - now else last calc - end of the past month),, + 'additional' => ['volume' => $business->files_volume], ]); } diff --git a/app/Http/Controllers/FileController.php b/app/Http/Controllers/FileController.php index a132732..23b30ca 100644 --- a/app/Http/Controllers/FileController.php +++ b/app/Http/Controllers/FileController.php @@ -9,8 +9,8 @@ use App\Models\Business; use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; +use App\Http\Resources\FileResource; use Illuminate\Support\Facades\Auth; -use App\HiLib\Resources\FileResource; use Spatie\QueryBuilder\QueryBuilder; use Spatie\QueryBuilder\AllowedFilter; use Illuminate\Support\Facades\Storage; @@ -127,6 +127,10 @@ class FileController extends Controller 'description' => $request->description ]); + $business->update([ + 'files_volume' => $business->files_volume + $file_record->size + ]); + return new FileResource($file_record); } diff --git a/app/Http/Resources/FileResource.php b/app/Http/Resources/FileResource.php index 3b74fb0..bc99db1 100644 --- a/app/Http/Resources/FileResource.php +++ b/app/Http/Resources/FileResource.php @@ -1,9 +1,7 @@ 'boolean', + 'calculated_at' => 'datetime', ]; public function getValueOf(?string $key) diff --git a/app/Models/Cost.php b/app/Models/Cost.php index fa06239..98b2e63 100644 --- a/app/Models/Cost.php +++ b/app/Models/Cost.php @@ -6,6 +6,10 @@ use App\Models\Model; class Cost extends Model { + public const USER_TYPE = 'users'; + + public const FILE_TYPE = 'files'; + public $perPage = 12; protected $fillable = [ diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index c02d1b6..69ff803 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -40,7 +40,7 @@ class AuthServiceProvider extends ServiceProvider 'token' => $request->bearerToken(), 'agent' => $request->getAgent(), 'os' => $request->getOS(), - ])->first(); + ])->firstOrFail(); return $fingerprint->user->setAttribute('token', $fingerprint->token); }); diff --git a/composer.json b/composer.json index a8d0129..6c87a76 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,8 @@ "jenssegers/agent": "^2.6", "laravel/socialite": "^5.1", "laravel/framework": "^8.12", + "laravel/legacy-factories": "^1", + "fruitcake/laravel-cors": "^2.0", "league/flysystem-aws-s3-v3": "~1.0", "spatie/laravel-medialibrary": "^9.0", "spatie/laravel-query-builder": "^3.3", From 51d2edd0d56dde2e0a275db9434415140902875a Mon Sep 17 00:00:00 2001 From: mahdihty Date: Sun, 7 Mar 2021 19:42:00 +0330 Subject: [PATCH 41/51] add event listener, notification and fa validations some change in enums --- app/Enums/cruds.php | 11 ++ app/Enums/tables.php | 51 +++++-- app/Events/TagCreate.php | 38 +++++ app/Listeners/NotifHandler.php | 36 +++++ app/Listeners/TagCreateNotif.php | 37 +++++ app/Models/User.php | 4 +- app/Notifications/MailNotification.php | 87 ++++++++++++ app/Providers/EventServiceProvider.php | 9 +- config/mail.php | 4 +- docker-compose.yml | 9 ++ resources/lang/fa/auth.php | 19 +++ resources/lang/fa/notification.php | 20 +++ resources/lang/fa/pagination.php | 19 +++ resources/lang/fa/passwords.php | 22 +++ resources/lang/fa/validation.php | 189 +++++++++++++++++++++++++ 15 files changed, 539 insertions(+), 16 deletions(-) create mode 100644 app/Enums/cruds.php create mode 100644 app/Events/TagCreate.php create mode 100644 app/Listeners/NotifHandler.php create mode 100644 app/Listeners/TagCreateNotif.php create mode 100644 app/Notifications/MailNotification.php create mode 100644 resources/lang/fa/auth.php create mode 100644 resources/lang/fa/notification.php create mode 100644 resources/lang/fa/pagination.php create mode 100644 resources/lang/fa/passwords.php create mode 100644 resources/lang/fa/validation.php diff --git a/app/Enums/cruds.php b/app/Enums/cruds.php new file mode 100644 index 0000000..1980e2e --- /dev/null +++ b/app/Enums/cruds.php @@ -0,0 +1,11 @@ + [ + 10 => ['name' => 'Create'], + 20 => ['name' => 'Update'], + 30 => ['name' => 'Delete'], + ], + +]; diff --git a/app/Enums/tables.php b/app/Enums/tables.php index 95a3d03..273bf4c 100644 --- a/app/Enums/tables.php +++ b/app/Enums/tables.php @@ -2,55 +2,82 @@ return [ + 'inverse' => [ + 10 => ['name' => 'Businesses'], + 20 => ['name' => 'Projects'], + 30 => ['name' => 'Sprints'], + 40 => ['name' => 'Statuses'], + 50 => ['name' => 'Systems'], + 60 => ['name' => 'Workflows'], + 70 => ['name' => 'Tags'], + 80 => ['name' => 'Tasks'], + 90 => ['name' => 'Works'], + 210 => ['name' => 'BusinessUser'], + 220 => ['name' => 'ProjectUser'], + 230 => ['name' => 'TagTask'], + ], + //Main Table's 'businesses' => [ 'id' => 10, - 'name' => 'Businesses' + 'name' => 'Businesses', + 'singular_name' => 'Business', ], 'projects' => [ 'id' => 20, - 'name' => 'Projects' + 'name' => 'Projects', + 'singular_name' => 'Project', ], 'sprints' => [ 'id' => 30, - 'name' => 'Sprints' + 'name' => 'Sprints', + 'singular_name' => 'Sprint', ], 'statuses' => [ 'id' => 40, - 'name' => 'Statuses' + 'name' => 'Statuses', + 'singular_name' => 'Status', ], 'systems' => [ 'id' => 50, - 'name' => 'Systems' + 'name' => 'Systems', + 'singular_name' => 'System', ], 'workflows' => [ 'id' => 60, - 'name' => 'Workflows' + 'name' => 'Workflows', + 'singular_name' => 'Workflow', ], 'tags' => [ 'id' => 70, - 'name' => 'Tags' + 'name' => 'Tags', + 'singular_name' => 'Tag', ], 'tasks' => [ 'id' => 80, - 'name' => 'Tasks' + 'name' => 'Tasks', + 'singular_name' => 'Task', ], 'works' => [ 'id' => 90, - 'name' => 'Works' + 'name' => 'Works', + 'singular_name' => 'Work', ], //Relation Table's 'business_user' => [ 'id' => 210, - 'name' => 'BusinessUser' + 'name' => 'BusinessUser', + 'singular_name' => 'BusinessUser', ], 'project_user' => [ 'id' => 220, - 'name' => 'ProjectUser' + 'name' => 'ProjectUser', + 'singular_name' => 'ProjectUser', ], 'tag_task' => [ 'id' => 230, - 'name' => 'TagTask' + 'name' => 'TagTask', + 'singular_name' => 'TagTask', ], ]; diff --git a/app/Events/TagCreate.php b/app/Events/TagCreate.php new file mode 100644 index 0000000..0a86866 --- /dev/null +++ b/app/Events/TagCreate.php @@ -0,0 +1,38 @@ +message = $message; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Listeners/NotifHandler.php b/app/Listeners/NotifHandler.php new file mode 100644 index 0000000..1fed4db --- /dev/null +++ b/app/Listeners/NotifHandler.php @@ -0,0 +1,36 @@ +message); + $event_class = 'App\Events\\'.enum('tables.'.$message->data->table_name.'.singular_name').enum('cruds.inverse.'.$message->data->crud_id.'.name'); + if (class_exists($event_class)) { +// event(new ('App\Events\\'.$event_class($message))); + $event_class::dispatch($message); + } + } +} diff --git a/app/Listeners/TagCreateNotif.php b/app/Listeners/TagCreateNotif.php new file mode 100644 index 0000000..8222723 --- /dev/null +++ b/app/Listeners/TagCreateNotif.php @@ -0,0 +1,37 @@ +message; + $notif_line = 'tags.create'; + $users = User::where('id', '<', 10)->get(); + Notification::send($users, new MailNotification($notif_line)); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 72c3b64..53d2106 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,6 +10,7 @@ use Illuminate\Http\UploadedFile; use Spatie\MediaLibrary\HasMedia; use App\Models\ReportableRelation; use Illuminate\Auth\Authenticatable; +use Illuminate\Notifications\Notifiable; use Spatie\MediaLibrary\InteractsWithMedia; use Illuminate\Foundation\Auth\Access\Authorizable; use Spatie\MediaLibrary\MediaCollections\Models\Media; @@ -18,7 +19,8 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; class User extends Model implements AuthenticatableContract, AuthorizableContract, HasMedia { - use SoftDeletes, + use Notifiable, + SoftDeletes, Authorizable, Authenticatable, InteractsWithMedia; diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php new file mode 100644 index 0000000..4122952 --- /dev/null +++ b/app/Notifications/MailNotification.php @@ -0,0 +1,87 @@ +message = $message; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line(__('notification.'.$this->message)) + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 4d702ef..c627395 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -3,7 +3,10 @@ namespace App\Providers; use App\Events\ModelSaved; +use App\Events\TagCreate; use App\Listeners\ActivityRegistration; +use App\Listeners\NotifHandler; +use App\Listeners\TagCreateNotif; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -21,7 +24,11 @@ class EventServiceProvider extends ServiceProvider SendEmailVerificationNotification::class, ], ModelSaved::class => [ - ActivityRegistration::class + ActivityRegistration::class, + NotifHandler::class, + ], + TagCreate::class => [ + TagCreateNotif::class, ] ]; diff --git a/config/mail.php b/config/mail.php index 54299aa..022418f 100644 --- a/config/mail.php +++ b/config/mail.php @@ -36,8 +36,8 @@ return [ 'mailers' => [ 'smtp' => [ 'transport' => 'smtp', - 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), + 'host' => env('MAIL_HOST', 'localhost'), + 'port' => env('MAIL_PORT', 1025), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), diff --git a/docker-compose.yml b/docker-compose.yml index 96ea2ee..d606ce7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -100,6 +100,15 @@ services: - mysql networks: - sail + mailhog: + image: mailhog/mailhog + logging: + driver: 'none' # disable saving logs + ports: + - 1025:1025 # smtp server + - 8025:8025 # web ui + networks: + - sail networks: sail: driver: bridge diff --git a/resources/lang/fa/auth.php b/resources/lang/fa/auth.php new file mode 100644 index 0000000..a5831f3 --- /dev/null +++ b/resources/lang/fa/auth.php @@ -0,0 +1,19 @@ + 'اطلاعات وارد شده صحیح نمی باشد', + 'throttle' => 'درخواست بیش از حد مجاز! لطفا بعد از :seconds ثانیه دوباره امتحان کنید', + +]; diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php new file mode 100644 index 0000000..e885bcc --- /dev/null +++ b/resources/lang/fa/notification.php @@ -0,0 +1,20 @@ + [ + 'create' => 'یک تگ ساخته شد.' + ] + +]; diff --git a/resources/lang/fa/pagination.php b/resources/lang/fa/pagination.php new file mode 100644 index 0000000..e00adbf --- /dev/null +++ b/resources/lang/fa/pagination.php @@ -0,0 +1,19 @@ + '« قبلی', + 'next' => 'بعدی »', + +]; diff --git a/resources/lang/fa/passwords.php b/resources/lang/fa/passwords.php new file mode 100644 index 0000000..671b232 --- /dev/null +++ b/resources/lang/fa/passwords.php @@ -0,0 +1,22 @@ + 'رمز عبور شما با موفقیت بازیابی شد', + 'sent' => 'ایمیلی برای بازیابی رمزعبور برای شما ارسال شد', + 'throttled' => 'لطفا اندکی صبر کنید', + 'token' => 'مشخصه بازیابی رمزعبور شما صحیح نمی باشد', + 'user' => "کاربری با این اطلاعات وجود ندارد", + +]; diff --git a/resources/lang/fa/validation.php b/resources/lang/fa/validation.php new file mode 100644 index 0000000..54b4e77 --- /dev/null +++ b/resources/lang/fa/validation.php @@ -0,0 +1,189 @@ + 'گزینه :attribute باید تایید شود', + 'active_url' => 'گزینه :attribute یک آدرس سایت معتبر نیست', + 'after' => 'گزینه :attribute باید تاریخی بعد از :date باشد', + 'after_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا بعد از :date باشد', + 'alpha' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'alpha_dash' => 'گزینه :attribute باید تنها شامل حروف، اعداد، خط تیره و زیر خط باشد', + 'alpha_num' => 'گزینه :attribute باید تنها شامل حروف و اعداد باشد', + 'array' => 'گزینه :attribute باید آرایه باشد', + 'before' => 'گزینه :attribute باید تاریخی قبل از :date باشد', + 'before_or_equal' => 'گزینه :attribute باید تاریخی مساوی یا قبل از :date باشد', + 'between' => [ + 'numeric' => 'گزینه :attribute باید بین :min و :max باشد', + 'file' => 'گزینه :attribute باید بین :min و :max کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بین :min و :max کاراکتر باشد', + 'array' => 'گزینه :attribute باید بین :min و :max آیتم باشد', + ], + 'boolean' => 'گزینه :attribute تنها می تواند صحیح یا غلط باشد', + 'confirmed' => 'تایید مجدد گزینه :attribute صحیح نمی باشد', + 'date' => 'گزینه :attribute یک تاریخ صحیح نمی باشد', + 'date_equals' => 'گزینه :attribute باید تاریخی مساوی با :date باشد', + 'date_format' => 'گزینه :attribute با فرمت :format همخوانی ندارد', + 'different' => 'گزینه :attribute و :other باید متفاوت باشند', + 'digits' => 'گزینه :attribute باید :digits عدد باشد', + 'digits_between' => 'گزینه :attribute باید بین :min و :max عدد باشد', + 'dimensions' => 'ابعاد تصویر گزینه :attribute مجاز نمی باشد', + 'distinct' => 'گزینه :attribute دارای افزونگی داده می باشد', + 'email' => 'گزینه :attribute باید یک آدرس ایمیل صحیح باشد', + 'ends_with' => 'گزینه :attribute باید با یکی از این مقادیر پایان یابد، :values', + 'exists' => 'گزینه انتخاب شده :attribute صحیح نمی باشد', + 'file' => 'گزینه :attribute باید یک فایل باشد', + 'filled' => 'گزینه :attribute نمی تواند خالی باشد', + 'gt' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر از :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید بیشتر از :value آیتم باشد', + ], + 'gte' => [ + 'numeric' => 'گزینه :attribute باید بزرگتر یا مساوی :value باشد', + 'file' => 'گزینه :attribute باید بزرگتر یا مساوی :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید بزرگتر یا مساوی :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید :value آیتم یا بیشتر داشته باشد', + ], + 'image' => 'گزینه :attribute باید از نوع تصویر باشد', + 'in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'in_array' => 'گزینه :attribute در :other وجود ندارد', + 'integer' => 'گزینه :attribute باید از نوع عددی باشد', + 'ip' => 'گزینه :attribute باید آی پی آدرس باشد', + 'ipv4' => 'گزینه :attribute باید آی پی آدرس ورژن 4 باشد', + 'ipv6' => 'گزینه :attribute باید آی پی آدرس ورژن 6 باشد', + 'json' => 'گزینه :attribute باید از نوع رشته جیسون باشد', + 'lt' => [ + 'numeric' => 'گزینه :attribute باید کمتر از :value باشد', + 'file' => 'گزینه :attribute باید کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute باید کمتر از :value آیتم داشته باشد', + ], + 'lte' => [ + 'numeric' => 'گزینه :attribute باید مساوی یا کمتر از :value باشد', + 'file' => 'گزینه :attribute باید مساوی یا کمتر از :value کیلوبایت باشد', + 'string' => 'گزینه :attribute باید مساوی یا کمتر از :value کاراکتر باشد', + 'array' => 'گزینه :attribute نباید کمتر از :value آیتم داشته باشد', + ], + 'max' => [ + 'numeric' => 'گزینه :attribute نباید بزرگتر از :max باشد', + 'file' => 'گزینه :attribute نباید بزرگتر از :max کیلوبایت باشد', + 'string' => 'گزینه :attribute نباید بزرگتر از :max کاراکتر باشد', + 'array' => 'گزینه :attribute نباید بیشتر از :max آیتم داشته باشد', + ], + 'mimes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'mimetypes' => 'گزینه :attribute باید دارای یکی از این فرمت ها باشد: :values', + 'min' => [ + 'numeric' => 'گزینه :attribute باید حداقل :min باشد', + 'file' => 'گزینه :attribute باید حداقل :min کیلوبایت باشد', + 'string' => 'گزینه :attribute باید حداقل :min کاراکتر باشد', + 'array' => 'گزینه :attribute باید حداقل :min آیتم داشته باشد', + ], + 'not_in' => 'گزینه انتخابی :attribute صحیح نمی باشد', + 'not_regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'numeric' => 'گزینه :attribute باید از نوع عددی باشد', + 'password' => 'رمزعبور صحیح نمی باشد', + 'present' => 'گزینه :attribute باید از نوع درصد باشد', + 'regex' => 'فرمت گزینه :attribute صحیح نمی باشد', + 'required' => 'تکمیل گزینه :attribute الزامی است', + 'required_if' => 'تکمیل گزینه :attribute زمانی که :other دارای مقدار :value است الزامی می باشد', + 'required_unless' => 'تکمیل گزینه :attribute الزامی می باشد مگر :other دارای مقدار :values باشد', + 'required_with' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد است الزامی است', + 'required_with_all' => 'تکمیل گزینه :attribute زمانی که مقادیر :values درصد است الزامی می باشد', + 'required_without' => 'تکمیل گزینه :attribute زمانی که مقدار :values درصد نیست الزامی است', + 'required_without_all' => 'تکمیل گزینه :attribute زمانی که هیچ کدام از مقادیر :values درصد نیست الزامی است', + 'same' => 'گزینه های :attribute و :other باید یکی باشند', + 'size' => [ + 'numeric' => 'گزینه :attribute باید :size باشد', + 'file' => 'گزینه :attribute باید :size کیلوبایت باشد', + 'string' => 'گزینه :attribute باید :size کاراکتر باشد', + 'array' => 'گزینه :attribute باید شامل :size آیتم باشد', + ], + 'starts_with' => 'گزینه :attribute باید با یکی از این مقادیر شروع شود، :values', + 'string' => 'گزینه :attribute باید تنها شامل حروف باشد', + 'timezone' => 'گزینه :attribute باید از نوع منطقه زمانی صحیح باشد', + 'unique' => 'این :attribute از قبل ثبت شده است', + 'uploaded' => 'آپلود گزینه :attribute شکست خورد', + 'url' => 'فرمت :attribute اشتباه است', + 'uuid' => 'گزینه :attribute باید یک UUID صحیح باشد', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [ + 'name' => 'نام', + 'username' => 'نام کاربری', + 'email' => 'ایمیل', + 'first_name' => 'نام', + 'last_name' => 'نام خانوادگی', + 'password' => 'رمز عبور', + 'password_confirmation' => 'تاییدیه رمز عبور', + 'city' => 'شهر', + 'state' => 'استان', + 'country' => 'کشور', + 'address' => 'آدرس', + 'phone' => 'تلفن', + 'mobile' => 'تلفن همراه', + 'age' => 'سن', + 'sex' => 'جنسیت', + 'gender' => 'جنسیت', + 'day' => 'روز', + 'month' => 'ماه', + 'year' => 'سال', + 'hour' => 'ساعت', + 'minute' => 'دقیقه', + 'second' => 'ثانیه', + 'title' => 'عنوان', + 'text' => 'متن', + 'content' => 'محتوا', + 'description' => 'توضیحات', + 'date' => 'تاریخ', + 'time' => 'زمان', + 'available' => 'موجود', + 'type' => 'نوع', + 'img' => 'تصویر', + 'image' => 'تصویر', + 'size' => 'اندازه', + 'color' => 'رنگ', + 'captcha' => 'کد امنیتی', + 'price' => 'قیمت', + 'pic' => 'تصویر', + 'link' => 'لینک', + ], +]; From 01c5ce3e388d8eff79fcab575e0081c4380ea736 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Mon, 8 Mar 2021 16:39:28 +0330 Subject: [PATCH 42/51] add some event, listener and fsm channel --- app/Channels/FcmChannel.php | 23 +++ app/Channels/Messages/FcmMessage.php | 132 ++++++++++++++++++ app/Enums/cruds.php | 6 +- app/Events/BusinessUserCreate.php | 38 +++++ app/Listeners/BusinessUserCreateNotif.php | 46 ++++++ app/Models/ReportableRelation.php | 32 +++-- app/Models/User.php | 10 ++ app/Notifications/DBNotification.php | 63 +++++++++ app/Notifications/FcmNotification.php | 48 +++++++ app/Notifications/MailNotification.php | 2 +- app/Providers/AppServiceProvider.php | 9 +- app/Providers/EventServiceProvider.php | 7 +- ...3_08_114700_create_notifications_table.php | 35 +++++ resources/lang/fa/notification.php | 4 + 14 files changed, 434 insertions(+), 21 deletions(-) create mode 100644 app/Channels/FcmChannel.php create mode 100644 app/Channels/Messages/FcmMessage.php create mode 100644 app/Events/BusinessUserCreate.php create mode 100644 app/Listeners/BusinessUserCreateNotif.php create mode 100644 app/Notifications/DBNotification.php create mode 100644 app/Notifications/FcmNotification.php create mode 100644 database/migrations/2021_03_08_114700_create_notifications_table.php diff --git a/app/Channels/FcmChannel.php b/app/Channels/FcmChannel.php new file mode 100644 index 0000000..e141bb4 --- /dev/null +++ b/app/Channels/FcmChannel.php @@ -0,0 +1,23 @@ +toFcm($notifiable); + + $recipients = $notifiable->routeNotificationFor('fcm', $notification); + } +} diff --git a/app/Channels/Messages/FcmMessage.php b/app/Channels/Messages/FcmMessage.php new file mode 100644 index 0000000..dc81a8a --- /dev/null +++ b/app/Channels/Messages/FcmMessage.php @@ -0,0 +1,132 @@ +to = $to[0]; + } else { + $this->to = $to; + } + + return $this; + } + + /** + * Set the topic of the FCM message. + * + * @param string $topic + * @return $this + */ + public function topic(string $topic) + { + $this->topic = $topic; + + return $this; + } + + /** + * Set the data of the FCM message. + * + * @param array $data + * @return $this + */ + public function data(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Set the notification of the FCM message. + * + * @param array $notification + * @return $this + */ + public function notification(array $notification) + { + $this->notification = $notification; + + return $this; + } + + /** + * Set the condition for receive the FCM message. + * + * @param string $condition + * @return $this + */ + public function condition(string $condition) + { + $this->condition = $condition; + + return $this; + } + + /** + * Set the priority of the FCM message. + * + * @param string $priority + * @return $this + */ + public function priority(string $priority) + { + $this->priority = $priority; + + return $this; + } +} diff --git a/app/Enums/cruds.php b/app/Enums/cruds.php index 1980e2e..283ea3b 100644 --- a/app/Enums/cruds.php +++ b/app/Enums/cruds.php @@ -3,9 +3,9 @@ return [ 'inverse' => [ - 10 => ['name' => 'Create'], - 20 => ['name' => 'Update'], - 30 => ['name' => 'Delete'], + 10 => ['name' => 'Create', 'singular_name' => 'create'], + 20 => ['name' => 'Update', 'singular_name' => 'update'], + 30 => ['name' => 'Delete', 'singular_name' => 'delete'], ], ]; diff --git a/app/Events/BusinessUserCreate.php b/app/Events/BusinessUserCreate.php new file mode 100644 index 0000000..b150a8d --- /dev/null +++ b/app/Events/BusinessUserCreate.php @@ -0,0 +1,38 @@ +message = $message; + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new PrivateChannel('channel-name'); + } +} diff --git a/app/Listeners/BusinessUserCreateNotif.php b/app/Listeners/BusinessUserCreateNotif.php new file mode 100644 index 0000000..caf33f0 --- /dev/null +++ b/app/Listeners/BusinessUserCreateNotif.php @@ -0,0 +1,46 @@ +message; + $new_user = User::findOrFail($payload->data->original->user_id); + $owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get(); + + $notif = [ + 'body' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name'), ['business' => request('_business_info')['name'], 'user' => $new_user->name]) + ]; + + $users = $owners->prepend($new_user); + Notification::send($users, new MailNotification($notif)); + Notification::send($users, new DBNotification($notif)); + } +} diff --git a/app/Models/ReportableRelation.php b/app/Models/ReportableRelation.php index b8b5d07..f525bf0 100644 --- a/app/Models/ReportableRelation.php +++ b/app/Models/ReportableRelation.php @@ -4,6 +4,7 @@ namespace App\Models; use Anik\Amqp\Exchange; use Anik\Amqp\Facades\Amqp; +use App\Events\ModelSaved; use PhpAmqpLib\Wire\AMQPTable; use Anik\Amqp\PublishableMessage; use Illuminate\Support\Facades\Auth; @@ -56,21 +57,22 @@ class ReportableRelation extends Pivot 'from' => env('CONTAINER_NAME'), ]; - $message = new PublishableMessage(json_encode($payload)); - - $routers = [ - "activity_exchange" => ["name" => "activity",], - "notif_exchange" => ["name" => "notif",], - "socket_exchange" => ["name" => "socket",], - ]; - - foreach ($routers as $exchange => $properties) { - $message->setProperties(["application_headers" => new AMQPTable($properties)]); - - $message->setExchange(new Exchange($exchange)); - - Amqp::publish($message, ""); - } + ModelSaved::dispatch(json_encode($payload)); +// $message = new PublishableMessage(json_encode($payload)); +// +// $routers = [ +// "activity_exchange" => ["name" => "activity",], +// "notif_exchange" => ["name" => "notif",], +// "socket_exchange" => ["name" => "socket",], +// ]; +// +// foreach ($routers as $exchange => $properties) { +// $message->setProperties(["application_headers" => new AMQPTable($properties)]); +// +// $message->setExchange(new Exchange($exchange)); +// +// Amqp::publish($message, ""); +// } } public function properties() diff --git a/app/Models/User.php b/app/Models/User.php index b1549d3..f6154ff 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -44,6 +44,16 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac public $detach_relation = false; + /** + * Specifies the user's FCM token + * + * @return string + */ + public function routeNotificationForFcm() + { + return $this->fingerprints()->fcm_token; + } + public function updateRelations() { // projects relations diff --git a/app/Notifications/DBNotification.php b/app/Notifications/DBNotification.php new file mode 100644 index 0000000..9c76954 --- /dev/null +++ b/app/Notifications/DBNotification.php @@ -0,0 +1,63 @@ +message = $message; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + 'data' => $this->message['body'] + ]; + } +} diff --git a/app/Notifications/FcmNotification.php b/app/Notifications/FcmNotification.php new file mode 100644 index 0000000..c302a64 --- /dev/null +++ b/app/Notifications/FcmNotification.php @@ -0,0 +1,48 @@ +to([]) + } + +} diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php index 4122952..abe937b 100644 --- a/app/Notifications/MailNotification.php +++ b/app/Notifications/MailNotification.php @@ -67,7 +67,7 @@ class MailNotification extends Notification //implements ShouldQueue public function toMail($notifiable) { return (new MailMessage) - ->line(__('notification.'.$this->message)) + ->line($this->message['body']) ->action('Notification Action', url('/')) ->line('Thank you for using our application!'); } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ee8ca5b..7b917de 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,9 @@ namespace App\Providers; +use App\Channels\FcmChannel; +use Illuminate\Notifications\ChannelManager; +use Illuminate\Support\Facades\Notification; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -13,7 +16,11 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + Notification::resolved(function (ChannelManager $service) { + $service->extend('fcm', function ($app) { + return new FcmChannel(new HttpClient, config('fcm.key')); + }); + }); } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index c627395..c3a0983 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,9 +2,11 @@ namespace App\Providers; +use App\Events\BusinessUserCreate; use App\Events\ModelSaved; use App\Events\TagCreate; use App\Listeners\ActivityRegistration; +use App\Listeners\BusinessUserCreateNotif; use App\Listeners\NotifHandler; use App\Listeners\TagCreateNotif; use Illuminate\Auth\Events\Registered; @@ -29,7 +31,10 @@ class EventServiceProvider extends ServiceProvider ], TagCreate::class => [ TagCreateNotif::class, - ] + ], + BusinessUserCreate::class => [ + BusinessUserCreateNotif::class, + ], ]; /** diff --git a/database/migrations/2021_03_08_114700_create_notifications_table.php b/database/migrations/2021_03_08_114700_create_notifications_table.php new file mode 100644 index 0000000..9797596 --- /dev/null +++ b/database/migrations/2021_03_08_114700_create_notifications_table.php @@ -0,0 +1,35 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notifications'); + } +} diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index e885bcc..d6e95b4 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -15,6 +15,10 @@ return [ 'tags' => [ 'create' => 'یک تگ ساخته شد.' + ], + + 'business_user' => [ + 'create' => 'کاربر :user به کسب و کار :business اضافه شد.' ] ]; From 1d3d111722d24804cd4e6bce2b96dd913fac3636 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Mon, 8 Mar 2021 18:20:06 +0330 Subject: [PATCH 43/51] some change in cors config --- config/cors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cors.php b/config/cors.php index 8a39e6d..b2930d0 100644 --- a/config/cors.php +++ b/config/cors.php @@ -21,7 +21,7 @@ return [ 'allowed_origins' => ['*'], - 'allowed_origins_patterns' => [], + 'allowed_origins_patterns' => ['Content-Type', 'X-Requested-With'], 'allowed_headers' => ['*'], From fc3e13643eb5aa4906737895aa9e5f1246d1b6ce Mon Sep 17 00:00:00 2001 From: akbarjimi Date: Mon, 8 Mar 2021 18:36:17 +0330 Subject: [PATCH 44/51] WIP --- app/Console/Commands/CostCommand.php | 10 +- app/Events/BusinessUpdate.php | 20 ++ app/Listeners/BusinessUpdateListener.php | 32 ++ app/Models/Model.php | 16 - app/Providers/EventServiceProvider.php | 15 +- composer.lock | 414 +++++++---------------- resources/lang/fa/notification.php | 7 +- 7 files changed, 208 insertions(+), 306 deletions(-) create mode 100644 app/Events/BusinessUpdate.php create mode 100644 app/Listeners/BusinessUpdateListener.php diff --git a/app/Console/Commands/CostCommand.php b/app/Console/Commands/CostCommand.php index 1e05de4..949442b 100644 --- a/app/Console/Commands/CostCommand.php +++ b/app/Console/Commands/CostCommand.php @@ -50,7 +50,7 @@ class CostCommand extends Command // if calculated_at less than an hour stop if ($now->diffInMinutes($business->calculated_at) <= 59) { $this->info('Must be one hour after the last audit.'); - Cache::put('lock', true, $now->diffInSeconds($business->calculated_at)); + $this->ensureLockIsWritten(); continue; } @@ -158,4 +158,12 @@ class CostCommand extends Command return $file_fee * $duration; } + + public function ensureLockIsWritten(): void + { + Cache::put('lock', true, 3600); + while (Cache::get('lock', false) === false) { + // prevent + } + } } diff --git a/app/Events/BusinessUpdate.php b/app/Events/BusinessUpdate.php new file mode 100644 index 0000000..f2ffa55 --- /dev/null +++ b/app/Events/BusinessUpdate.php @@ -0,0 +1,20 @@ +message = $message; + } +} diff --git a/app/Listeners/BusinessUpdateListener.php b/app/Listeners/BusinessUpdateListener.php new file mode 100644 index 0000000..3ef51cb --- /dev/null +++ b/app/Listeners/BusinessUpdateListener.php @@ -0,0 +1,32 @@ +message; + + $wallet = $payload?->data?->diff?->wallet; + $owners = Business::findOrFail($payload->business)->owners; + $message = ['body' => 'Test']; + + if ($wallet < 0) { + Notification::send($owners, new MailNotification($message)); + Notification::send($owners, new DBNotification($message)); + } + } +} diff --git a/app/Models/Model.php b/app/Models/Model.php index b2f1373..e47e36d 100644 --- a/app/Models/Model.php +++ b/app/Models/Model.php @@ -191,23 +191,7 @@ class Model extends EloquentModel 'from' => env('CONTAINER_NAME'), ]; -// $message = new PublishableMessage(json_encode($payload)); - ModelSaved::dispatch(json_encode($payload)); - -// $routers = [ -// "activity_exchange" => ["name" => "activity",], -// "notif_exchange" => ["name" => "notif",], -// "socket_exchange" => ["name" => "socket",], -// ]; -// -// foreach ($routers as $exchange => $properties) { -// $message->setProperties(["application_headers" => new AMQPTable($properties)]); -// -// $message->setExchange(new Exchange($exchange)); -// -// Amqp::publish($message, ""); -// } } /** diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index c3a0983..ecfdd03 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,17 +2,19 @@ namespace App\Providers; -use App\Events\BusinessUserCreate; -use App\Events\ModelSaved; use App\Events\TagCreate; -use App\Listeners\ActivityRegistration; -use App\Listeners\BusinessUserCreateNotif; +use App\Events\ModelSaved; +use App\Events\BusinessUpdate; use App\Listeners\NotifHandler; use App\Listeners\TagCreateNotif; +use App\Events\BusinessUserCreate; +use Illuminate\Support\Facades\Event; use Illuminate\Auth\Events\Registered; +use App\Listeners\ActivityRegistration; +use App\Listeners\BusinessUpdateListener; +use App\Listeners\BusinessUserCreateNotif; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; -use Illuminate\Support\Facades\Event; class EventServiceProvider extends ServiceProvider { @@ -35,6 +37,9 @@ class EventServiceProvider extends ServiceProvider BusinessUserCreate::class => [ BusinessUserCreateNotif::class, ], + BusinessUpdate::class => [ + BusinessUpdateListener::class, + ], ]; /** diff --git a/composer.lock b/composer.lock index 59f8671..89d7c77 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e612fae4cbef3c536c8e43cb7732d4cb", + "content-hash": "5fa03c0090cecfc0c45ad964962ea582", "packages": [ { "name": "anik/amqp", @@ -119,16 +119,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.173.20", + "version": "3.173.23", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "3e6143f5c12986d727307d5d19d6aec21575d903" + "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3e6143f5c12986d727307d5d19d6aec21575d903", - "reference": "3e6143f5c12986d727307d5d19d6aec21575d903", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/259fa1a47a46f81e66b1ea328ed961a58fa061c2", + "reference": "259fa1a47a46f81e66b1ea328ed961a58fa061c2", "shasum": "" }, "require": { @@ -203,9 +203,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.173.20" + "source": "https://github.com/aws/aws-sdk-php/tree/3.173.23" }, - "time": "2021-03-02T19:13:50+00:00" + "time": "2021-03-05T19:20:35+00:00" }, { "name": "beberlei/assert", @@ -974,16 +974,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "60d379c243457e073cff02bc323a2a86cb355631" + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631", - "reference": "60d379c243457e073cff02bc323a2a86cb355631", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { @@ -1023,9 +1023,9 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.0" + "source": "https://github.com/guzzle/promises/tree/1.4.1" }, - "time": "2020-09-30T07:37:28+00:00" + "time": "2021-03-07T09:25:29+00:00" }, { "name": "guzzlehttp/psr7", @@ -1178,16 +1178,16 @@ }, { "name": "jaybizzle/crawler-detect", - "version": "v1.2.104", + "version": "v1.2.105", "source": { "type": "git", "url": "https://github.com/JayBizzle/Crawler-Detect.git", - "reference": "a581e89a9212c4e9d18049666dc735718c29de9c" + "reference": "719c1ed49224857800c3dc40838b6b761d046105" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/a581e89a9212c4e9d18049666dc735718c29de9c", - "reference": "a581e89a9212c4e9d18049666dc735718c29de9c", + "url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/719c1ed49224857800c3dc40838b6b761d046105", + "reference": "719c1ed49224857800c3dc40838b6b761d046105", "shasum": "" }, "require": { @@ -1224,9 +1224,9 @@ ], "support": { "issues": "https://github.com/JayBizzle/Crawler-Detect/issues", - "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.104" + "source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.2.105" }, - "time": "2021-01-13T15:25:20+00:00" + "time": "2021-03-03T20:55:48+00:00" }, { "name": "jenssegers/agent", @@ -1313,16 +1313,16 @@ }, { "name": "laravel/framework", - "version": "v8.30.0", + "version": "v8.31.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "81ef9850cc388f2f92b868fb35ffb76f0c9a0f46" + "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/81ef9850cc388f2f92b868fb35ffb76f0c9a0f46", - "reference": "81ef9850cc388f2f92b868fb35ffb76f0c9a0f46", + "url": "https://api.github.com/repos/laravel/framework/zipball/2aa5c2488d25178ebc097052c7897a0e463ddc35", + "reference": "2aa5c2488d25178ebc097052c7897a0e463ddc35", "shasum": "" }, "require": { @@ -1477,7 +1477,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2021-03-02T14:28:44+00:00" + "time": "2021-03-04T15:22:36+00:00" }, { "name": "laravel/helpers", @@ -1591,103 +1591,6 @@ }, "time": "2020-10-27T14:25:32+00:00" }, - { - "name": "laravel/lumen-framework", - "version": "v8.2.3", - "source": { - "type": "git", - "url": "https://github.com/laravel/lumen-framework.git", - "reference": "6ed02d4d1a6e203b9e896bd105b2e838866f2951" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/lumen-framework/zipball/6ed02d4d1a6e203b9e896bd105b2e838866f2951", - "reference": "6ed02d4d1a6e203b9e896bd105b2e838866f2951", - "shasum": "" - }, - "require": { - "dragonmantank/cron-expression": "^3.0.2", - "illuminate/auth": "^8.0", - "illuminate/broadcasting": "^8.0", - "illuminate/bus": "^8.0", - "illuminate/cache": "^8.0", - "illuminate/collections": "^8.0", - "illuminate/config": "^8.0", - "illuminate/console": "^8.0", - "illuminate/container": "^8.0", - "illuminate/contracts": "^8.0", - "illuminate/database": "^8.0", - "illuminate/encryption": "^8.0", - "illuminate/events": "^8.0", - "illuminate/filesystem": "^8.0", - "illuminate/hashing": "^8.0", - "illuminate/http": "^8.0", - "illuminate/log": "^8.0", - "illuminate/macroable": "^8.0", - "illuminate/pagination": "^8.0", - "illuminate/pipeline": "^8.0", - "illuminate/queue": "^8.0", - "illuminate/support": "^8.0", - "illuminate/testing": "^8.0", - "illuminate/translation": "^8.0", - "illuminate/validation": "^8.0", - "illuminate/view": "^8.0", - "nikic/fast-route": "^1.3", - "php": "^7.3|^8.0", - "symfony/console": "^5.1", - "symfony/error-handler": "^5.1", - "symfony/http-foundation": "^5.1", - "symfony/http-kernel": "^5.1", - "symfony/mime": "^5.1", - "symfony/var-dumper": "^5.1", - "vlucas/phpdotenv": "^5.2" - }, - "require-dev": { - "mockery/mockery": "^1.4.2", - "phpunit/phpunit": "^8.5.8|^9.3.3" - }, - "suggest": { - "laravel/tinker": "Required to use the tinker console command (^2.0).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\Lumen\\": "src/" - }, - "files": [ - "src/helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "The Laravel Lumen Framework.", - "homepage": "https://lumen.laravel.com", - "keywords": [ - "framework", - "laravel", - "lumen" - ], - "support": { - "issues": "https://github.com/laravel/lumen-framework/issues", - "source": "https://github.com/laravel/lumen-framework" - }, - "time": "2021-02-09T16:42:36+00:00" - }, { "name": "laravel/socialite", "version": "v5.2.2", @@ -2730,16 +2633,16 @@ }, { "name": "nesbot/carbon", - "version": "2.45.1", + "version": "2.46.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d" + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/528783b188bdb853eb21239b1722831e0f000a8d", - "reference": "528783b188bdb853eb21239b1722831e0f000a8d", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", + "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4", "shasum": "" }, "require": { @@ -2819,57 +2722,7 @@ "type": "tidelift" } ], - "time": "2021-02-11T18:30:17+00:00" - }, - { - "name": "nikic/fast-route", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/FastRoute.git", - "reference": "181d480e08d9476e61381e04a71b34dc0432e812" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", - "reference": "181d480e08d9476e61381e04a71b34dc0432e812", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35|~5.7" - }, - "type": "library", - "autoload": { - "psr-4": { - "FastRoute\\": "src/" - }, - "files": [ - "src/functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov", - "email": "nikic@php.net" - } - ], - "description": "Fast request router for PHP", - "keywords": [ - "router", - "routing" - ], - "support": { - "issues": "https://github.com/nikic/FastRoute/issues", - "source": "https://github.com/nikic/FastRoute/tree/master" - }, - "time": "2018-02-13T20:26:39+00:00" + "time": "2021-02-24T17:30:44+00:00" }, { "name": "nikic/php-parser", @@ -3302,27 +3155,22 @@ }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -3335,7 +3183,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -3349,9 +3197,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2017-02-14T16:28:37+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/event-dispatcher", @@ -4021,16 +3869,16 @@ }, { "name": "spatie/laravel-medialibrary", - "version": "9.4.2", + "version": "9.4.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "fe8dcb2a56b3061cd29d072c1e983a1e035a1671" + "reference": "2eae7416a6d7762e147f26f6cac7cf099eda109b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/fe8dcb2a56b3061cd29d072c1e983a1e035a1671", - "reference": "fe8dcb2a56b3061cd29d072c1e983a1e035a1671", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/2eae7416a6d7762e147f26f6cac7cf099eda109b", + "reference": "2eae7416a6d7762e147f26f6cac7cf099eda109b", "shasum": "" }, "require": { @@ -4109,7 +3957,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/9.4.2" + "source": "https://github.com/spatie/laravel-medialibrary/tree/9.4.3" }, "funding": [ { @@ -4121,7 +3969,7 @@ "type": "github" } ], - "time": "2021-01-15T07:55:54+00:00" + "time": "2021-03-07T18:42:19+00:00" }, { "name": "spatie/laravel-query-builder", @@ -4246,16 +4094,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v6.2.5", + "version": "v6.2.6", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7" + "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/698a6a9f54d7eb321274de3ad19863802c879fb7", - "reference": "698a6a9f54d7eb321274de3ad19863802c879fb7", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d2791ff0b73247cdc2096b14f5580aba40c12bff", + "reference": "d2791ff0b73247cdc2096b14f5580aba40c12bff", "shasum": "" }, "require": { @@ -4305,7 +4153,7 @@ ], "support": { "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.5" + "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.2.6" }, "funding": [ { @@ -4317,20 +4165,20 @@ "type": "tidelift" } ], - "time": "2021-01-12T09:35:59+00:00" + "time": "2021-03-05T12:08:49+00:00" }, { "name": "symfony/console", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a" + "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a", + "url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", + "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", "shasum": "" }, "require": { @@ -4398,7 +4246,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.3" + "source": "https://github.com/symfony/console/tree/v5.2.4" }, "funding": [ { @@ -4414,11 +4262,11 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-23T10:08:49+00:00" }, { "name": "symfony/css-selector", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -4463,7 +4311,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.2.3" + "source": "https://github.com/symfony/css-selector/tree/v5.2.4" }, "funding": [ { @@ -4550,16 +4398,16 @@ }, { "name": "symfony/error-handler", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "48f18b3609e120ea66d59142c23dc53e9562c26d" + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/48f18b3609e120ea66d59142c23dc53e9562c26d", - "reference": "48f18b3609e120ea66d59142c23dc53e9562c26d", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/b547d3babcab5c31e01de59ee33e9d9c1421d7d0", + "reference": "b547d3babcab5c31e01de59ee33e9d9c1421d7d0", "shasum": "" }, "require": { @@ -4599,7 +4447,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v5.2.3" + "source": "https://github.com/symfony/error-handler/tree/v5.2.4" }, "funding": [ { @@ -4615,20 +4463,20 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-11T08:21:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367" + "reference": "d08d6ec121a425897951900ab692b612a61d6240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4f9760f8074978ad82e2ce854dff79a71fe45367", - "reference": "4f9760f8074978ad82e2ce854dff79a71fe45367", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", + "reference": "d08d6ec121a425897951900ab692b612a61d6240", "shasum": "" }, "require": { @@ -4684,7 +4532,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.3" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" }, "funding": [ { @@ -4700,7 +4548,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:36:42+00:00" + "time": "2021-02-18T17:12:37+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4783,16 +4631,16 @@ }, { "name": "symfony/finder", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4adc8d172d602008c204c2e16956f99257248e03" + "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4adc8d172d602008c204c2e16956f99257248e03", - "reference": "4adc8d172d602008c204c2e16956f99257248e03", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", "shasum": "" }, "require": { @@ -4824,7 +4672,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.2.3" + "source": "https://github.com/symfony/finder/tree/v5.2.4" }, "funding": [ { @@ -4840,7 +4688,7 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/http-client-contracts", @@ -4923,16 +4771,16 @@ }, { "name": "symfony/http-foundation", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "20c554c0f03f7cde5ce230ed248470cccbc34c36" + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/20c554c0f03f7cde5ce230ed248470cccbc34c36", - "reference": "20c554c0f03f7cde5ce230ed248470cccbc34c36", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/54499baea7f7418bce7b5ec92770fd0799e8e9bf", + "reference": "54499baea7f7418bce7b5ec92770fd0799e8e9bf", "shasum": "" }, "require": { @@ -4976,7 +4824,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v5.2.3" + "source": "https://github.com/symfony/http-foundation/tree/v5.2.4" }, "funding": [ { @@ -4992,20 +4840,20 @@ "type": "tidelift" } ], - "time": "2021-02-03T04:42:09+00:00" + "time": "2021-02-25T17:16:57+00:00" }, { "name": "symfony/http-kernel", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05" + "reference": "c452dbe4f385f030c3957821bf921b13815d6140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05", - "reference": "89bac04f29e7b0b52f9fa6a4288ca7a8f90a1a05", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c452dbe4f385f030c3957821bf921b13815d6140", + "reference": "c452dbe4f385f030c3957821bf921b13815d6140", "shasum": "" }, "require": { @@ -5040,7 +4888,7 @@ "psr/log-implementation": "1.0" }, "require-dev": { - "psr/cache": "~1.0", + "psr/cache": "^1.0|^2.0|^3.0", "symfony/browser-kit": "^4.4|^5.0", "symfony/config": "^5.0", "symfony/console": "^4.4|^5.0", @@ -5088,7 +4936,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v5.2.3" + "source": "https://github.com/symfony/http-kernel/tree/v5.2.4" }, "funding": [ { @@ -5104,20 +4952,20 @@ "type": "tidelift" } ], - "time": "2021-02-03T04:51:58+00:00" + "time": "2021-03-04T18:05:55+00:00" }, { "name": "symfony/mime", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86" + "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", - "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", + "url": "https://api.github.com/repos/symfony/mime/zipball/5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", + "reference": "5155d2fe14ef1eb150e3bdbbc1ec1455df95e9cd", "shasum": "" }, "require": { @@ -5170,7 +5018,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v5.2.3" + "source": "https://github.com/symfony/mime/tree/v5.2.4" }, "funding": [ { @@ -5186,7 +5034,7 @@ "type": "tidelift" } ], - "time": "2021-02-02T06:10:15+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5919,7 +5767,7 @@ }, { "name": "symfony/process", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -5961,7 +5809,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.2.3" + "source": "https://github.com/symfony/process/tree/v5.2.4" }, "funding": [ { @@ -5981,16 +5829,16 @@ }, { "name": "symfony/routing", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661" + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/348b5917e56546c6d96adbf21d7f92c9ef563661", - "reference": "348b5917e56546c6d96adbf21d7f92c9ef563661", + "url": "https://api.github.com/repos/symfony/routing/zipball/cafa138128dfd6ab6be1abf6279169957b34f662", + "reference": "cafa138128dfd6ab6be1abf6279169957b34f662", "shasum": "" }, "require": { @@ -6051,7 +5899,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v5.2.3" + "source": "https://github.com/symfony/routing/tree/v5.2.4" }, "funding": [ { @@ -6067,7 +5915,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-02-22T15:48:39+00:00" }, { "name": "symfony/service-contracts", @@ -6150,16 +5998,16 @@ }, { "name": "symfony/string", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "c95468897f408dd0aca2ff582074423dd0455122" + "reference": "4e78d7d47061fa183639927ec40d607973699609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", - "reference": "c95468897f408dd0aca2ff582074423dd0455122", + "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", + "reference": "4e78d7d47061fa183639927ec40d607973699609", "shasum": "" }, "require": { @@ -6213,7 +6061,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.3" + "source": "https://github.com/symfony/string/tree/v5.2.4" }, "funding": [ { @@ -6229,20 +6077,20 @@ "type": "tidelift" } ], - "time": "2021-01-25T15:14:59+00:00" + "time": "2021-02-16T10:20:28+00:00" }, { "name": "symfony/translation", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949" + "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/c021864d4354ee55160ddcfd31dc477a1bc77949", - "reference": "c021864d4354ee55160ddcfd31dc477a1bc77949", + "url": "https://api.github.com/repos/symfony/translation/zipball/74b0353ab34ff4cca827a2cf909e325d96815e60", + "reference": "74b0353ab34ff4cca827a2cf909e325d96815e60", "shasum": "" }, "require": { @@ -6259,7 +6107,7 @@ "symfony/yaml": "<4.4" }, "provide": { - "symfony/translation-implementation": "2.0" + "symfony/translation-implementation": "2.3" }, "require-dev": { "psr/log": "~1.0", @@ -6306,7 +6154,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.2.3" + "source": "https://github.com/symfony/translation/tree/v5.2.4" }, "funding": [ { @@ -6322,7 +6170,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-03-04T15:41:09+00:00" }, { "name": "symfony/translation-contracts", @@ -6404,16 +6252,16 @@ }, { "name": "symfony/var-dumper", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "72ca213014a92223a5d18651ce79ef441c12b694" + "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/72ca213014a92223a5d18651ce79ef441c12b694", - "reference": "72ca213014a92223a5d18651ce79ef441c12b694", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6a81fec0628c468cf6d5c87a4d003725e040e223", + "reference": "6a81fec0628c468cf6d5c87a4d003725e040e223", "shasum": "" }, "require": { @@ -6472,7 +6320,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.2.3" + "source": "https://github.com/symfony/var-dumper/tree/v5.2.4" }, "funding": [ { @@ -6488,7 +6336,7 @@ "type": "tidelift" } ], - "time": "2021-01-27T10:15:41+00:00" + "time": "2021-02-18T23:11:19+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -7031,16 +6879,16 @@ }, { "name": "facade/ignition", - "version": "2.5.13", + "version": "2.5.14", "source": { "type": "git", "url": "https://github.com/facade/ignition.git", - "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e" + "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facade/ignition/zipball/5e9ef386aaad9985cee2ac23281a27568d083b7e", - "reference": "5e9ef386aaad9985cee2ac23281a27568d083b7e", + "url": "https://api.github.com/repos/facade/ignition/zipball/17097f7a83e200d90d1cf9f4d1b35c1001513a47", + "reference": "17097f7a83e200d90d1cf9f4d1b35c1001513a47", "shasum": "" }, "require": { @@ -7104,7 +6952,7 @@ "issues": "https://github.com/facade/ignition/issues", "source": "https://github.com/facade/ignition" }, - "time": "2021-02-16T12:46:19+00:00" + "time": "2021-03-04T08:48:01+00:00" }, { "name": "facade/ignition-contracts", @@ -7335,16 +7183,16 @@ }, { "name": "laravel/sail", - "version": "v1.4.4", + "version": "v1.4.6", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "67b76f430f8870f5a2384db5c87ff8321154e077" + "reference": "59ee7e2b2efeb644eabea719186db91d11666733" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/67b76f430f8870f5a2384db5c87ff8321154e077", - "reference": "67b76f430f8870f5a2384db5c87ff8321154e077", + "url": "https://api.github.com/repos/laravel/sail/zipball/59ee7e2b2efeb644eabea719186db91d11666733", + "reference": "59ee7e2b2efeb644eabea719186db91d11666733", "shasum": "" }, "require": { @@ -7391,7 +7239,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2021-03-02T16:47:59+00:00" + "time": "2021-03-03T15:22:44+00:00" }, { "name": "mockery/mockery", diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index d6e95b4..9c80b20 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -19,6 +19,11 @@ return [ 'business_user' => [ 'create' => 'کاربر :user به کسب و کار :business اضافه شد.' - ] + ], + + 'business' => [ + 'update' => 'کاربر :user به کسب و کار :business اضافه شد.', + 'update_wallet' => 'پول رو میدی یا پولت کنم', + ], ]; From 1d0231f6f3425efe4c39d6f99b6e077930d3d5d1 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Mon, 8 Mar 2021 19:04:39 +0330 Subject: [PATCH 45/51] complete Fcm push notification channel, message, notification add fcm_token to Fingerprint model --- app/Notifications/MailNotification.php | 5 +++-- config/fcm.php | 18 ++++++++++++++++++ resources/lang/fa/notification.php | 7 ++++++- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 config/fcm.php diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php index abe937b..af60d57 100644 --- a/app/Notifications/MailNotification.php +++ b/app/Notifications/MailNotification.php @@ -67,9 +67,10 @@ class MailNotification extends Notification //implements ShouldQueue public function toMail($notifiable) { return (new MailMessage) + ->greeting($this->message['greeting']) ->line($this->message['body']) - ->action('Notification Action', url('/')) - ->line('Thank you for using our application!'); + ->subject($this->message['subject']) + ->action('Notification Action', url('/')); } /** diff --git a/config/fcm.php b/config/fcm.php new file mode 100644 index 0000000..9105e5e --- /dev/null +++ b/config/fcm.php @@ -0,0 +1,18 @@ + env('FCM_KEY'), + +]; diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index d6e95b4..6a7fb83 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -18,7 +18,12 @@ return [ ], 'business_user' => [ - 'create' => 'کاربر :user به کسب و کار :business اضافه شد.' + 'create' =>[ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن کاربر به کسب و کار', + 'title' => 'افزودن کاربر به کسب و کار', + 'body' => 'کاربر :user به کسب و کار :business اضافه شد.' + ] ] ]; From 0ea97f895b034692be3ad8f108952564f44fbe57 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Mon, 8 Mar 2021 19:04:46 +0330 Subject: [PATCH 46/51] complete Fcm push notification channel, message, notification add fcm_token to Fingerprint model --- app/Channels/FcmChannel.php | 72 ++++++++++++++++++- app/Listeners/BusinessUserCreateNotif.php | 8 ++- app/Models/Fingerprint.php | 3 +- app/Models/User.php | 10 +++ app/Notifications/FcmNotification.php | 17 +++-- app/Providers/AppServiceProvider.php | 1 + .../2020_08_18_085017_fingerprints.php | 1 + 7 files changed, 102 insertions(+), 10 deletions(-) diff --git a/app/Channels/FcmChannel.php b/app/Channels/FcmChannel.php index e141bb4..382bfcf 100644 --- a/app/Channels/FcmChannel.php +++ b/app/Channels/FcmChannel.php @@ -3,10 +3,46 @@ namespace App\Channels; +use App\Channels\Messages\FcmMessage; use Illuminate\Notifications\Notification; +use GuzzleHttp\Client as HttpClient; class FcmChannel { + /** + * The API URL for FCM. + * + * @var string + */ + const API_URI = 'https://fcm.googleapis.com/fcm/send'; + + /** + * The HTTP client instance. + * + * @var \GuzzleHttp\Client + */ + protected $http; + + /** + * The FCM API key. + * + * @var string + */ + protected $apikey; + + /** + * Create a new FCM channel instance. + * + * @param \GuzzleHttp\Client $http + * @param string $apiKey + * @return void + */ + public function __construct(HttpClient $http, string $apiKey) + { + $this->http = $http; + $this->apiKey = $apiKey; + } + /** * Send the given notification. * @@ -18,6 +54,40 @@ class FcmChannel { $message = $notification->toFcm($notifiable); - $recipients = $notifiable->routeNotificationFor('fcm', $notification); + $message->to($notifiable->routeNotificationFor('fcm', $notification)); + + if (! $this->apiKey || (! $message->topic && ! $message->to)) { + return; + } + + $this->http->post(self::API_URI, [ + 'headers' => [ + 'Authorization' => "key={$this->apiKey}", + 'Content-Type' => 'application/json', + ], + 'json' => $this->buildJsonPayload($message), + ]); + } + + protected function buildJsonPayload(FcmMessage $message) + { + $payload = array_filter([ + 'priority' => $message->priority, + 'data' => $message->data, + 'notification' => $message->notification, + 'condition' => $message->condition, + ]); + + if ($message->topic) { + $payload['to'] = "/topics/{$message->topic}"; + } else { + if (is_array($message->to)) { + $payload['registration_ids'] = $message->to; + } else { + $payload['to'] = $message->to; + } + } + + return $payload; } } diff --git a/app/Listeners/BusinessUserCreateNotif.php b/app/Listeners/BusinessUserCreateNotif.php index caf33f0..41a3ab5 100644 --- a/app/Listeners/BusinessUserCreateNotif.php +++ b/app/Listeners/BusinessUserCreateNotif.php @@ -2,10 +2,12 @@ namespace App\Listeners; +use App\Channels\FcmChannel; use App\Events\BusinessUserCreate; use App\Models\Business; use App\Models\User; use App\Notifications\DBNotification; +use App\Notifications\FcmNotification; use App\Notifications\MailNotification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; @@ -36,11 +38,15 @@ class BusinessUserCreateNotif $owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get(); $notif = [ - 'body' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name'), ['business' => request('_business_info')['name'], 'user' => $new_user->name]) + 'greeting' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.greeting'), + 'subject' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.subject'), + 'title' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.title'), + 'body' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.body', ['business' => request('_business_info')['name'], 'user' => $new_user->name]) ]; $users = $owners->prepend($new_user); Notification::send($users, new MailNotification($notif)); Notification::send($users, new DBNotification($notif)); + Notification::send($users, new FcmNotification($notif)); } } diff --git a/app/Models/Fingerprint.php b/app/Models/Fingerprint.php index 8db0c86..f6cb0d4 100644 --- a/app/Models/Fingerprint.php +++ b/app/Models/Fingerprint.php @@ -6,7 +6,7 @@ use App\Models\Model; class Fingerprint extends Model { - protected $fillable = ['user_id', 'agent', 'ip', 'os', 'latitude', 'longitude', 'token',]; + protected $fillable = ['user_id', 'agent', 'ip', 'os', 'latitude', 'longitude', 'token', 'fcm_token']; protected $table = 'fingerprints'; @@ -25,6 +25,7 @@ class Fingerprint extends Model 'latitude' => 'required', 'longitude' => 'required', 'token' => 'required|string|min:60', + 'fcm_token' => 'nullable', ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index 52767e4..3479edf 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -43,6 +43,16 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac public $detach_relation = false; + /** + * Specifies the user's FCM token + * + * @return string + */ + public function routeNotificationForFcm() + { + return $this->fingerprints->whereNotNull('fcm_token')->pluck('fcm_token')->all(); + } + public function updateRelations() { // projects relations diff --git a/app/Notifications/FcmNotification.php b/app/Notifications/FcmNotification.php index c302a64..cfbd38a 100644 --- a/app/Notifications/FcmNotification.php +++ b/app/Notifications/FcmNotification.php @@ -4,22 +4,22 @@ namespace App\Notifications; use App\Channels\Messages\FcmMessage; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; class FcmNotification extends Notification { use Queueable; + public $message; + /** * Create a new notification instance. * * @return void */ - public function __construct() + public function __construct($message) { - // + $this->message = $message; } /** @@ -30,7 +30,7 @@ class FcmNotification extends Notification */ public function via($notifiable) { - return [FcmNotification::class]; + return ['fcm']; } /** @@ -41,8 +41,11 @@ class FcmNotification extends Notification */ public function toFcm($notifiable) { -// return (new FcmMessage()) -// ->to([]) + return (new FcmMessage()) + ->data([ + 'title' => $this->message['title'], + 'body' => $this->message['body'], + ]); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 7b917de..2dae397 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,6 +6,7 @@ use App\Channels\FcmChannel; use Illuminate\Notifications\ChannelManager; use Illuminate\Support\Facades\Notification; use Illuminate\Support\ServiceProvider; +use GuzzleHttp\Client as HttpClient; class AppServiceProvider extends ServiceProvider { diff --git a/database/migrations/2020_08_18_085017_fingerprints.php b/database/migrations/2020_08_18_085017_fingerprints.php index af506ec..6a72833 100644 --- a/database/migrations/2020_08_18_085017_fingerprints.php +++ b/database/migrations/2020_08_18_085017_fingerprints.php @@ -22,6 +22,7 @@ class Fingerprints extends Migration $table->decimal('latitude', 10, 4); $table->decimal('longitude', 11, 4); $table->char('token', 60)->unique(); + $table->text('fcm_token')->unique()->nullable(); $table->timestamps(); }); } From 6de9a528cc1f25003f91833745496d542a57e73f Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 9 Mar 2021 10:56:15 +0330 Subject: [PATCH 47/51] clean code in BusinessUserCreateNotif.php --- app/Listeners/BusinessUserCreateNotif.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/Listeners/BusinessUserCreateNotif.php b/app/Listeners/BusinessUserCreateNotif.php index 41a3ab5..a7b06d0 100644 --- a/app/Listeners/BusinessUserCreateNotif.php +++ b/app/Listeners/BusinessUserCreateNotif.php @@ -38,10 +38,10 @@ class BusinessUserCreateNotif $owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get(); $notif = [ - 'greeting' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.greeting'), - 'subject' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.subject'), - 'title' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.title'), - 'body' => __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.body', ['business' => request('_business_info')['name'], 'user' => $new_user->name]) + 'greeting' => $this->getMessageLine($payload, 'greeting'), + 'subject' => $this->getMessageLine($payload, 'subject'), + 'title' => $this->getMessageLine($payload, 'title'), + 'body' => $this->getMessageLine($payload, 'body', ['business' => request('_business_info')['name'], 'user' => $new_user->name]) ]; $users = $owners->prepend($new_user); @@ -49,4 +49,18 @@ class BusinessUserCreateNotif Notification::send($users, new DBNotification($notif)); Notification::send($users, new FcmNotification($notif)); } + + /** + * Fetch message from notifications lang file + * + * @param $payload + * @param $key + * @param null $options + * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null + * + */ + public function getMessageLine($payload, $key, $options = null) + { + return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options); + } } From 8bc2d8e3d7ab1df662b651a7ee5a0782f6a2d434 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 9 Mar 2021 13:30:38 +0330 Subject: [PATCH 48/51] some minor change and clean code --- app/Listeners/BusinessUserCreateNotif.php | 42 ++++++++++++++++++----- app/Listeners/ProjectUserCreateNotif.php | 7 ++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/app/Listeners/BusinessUserCreateNotif.php b/app/Listeners/BusinessUserCreateNotif.php index 5beb17d..29f4456 100644 --- a/app/Listeners/BusinessUserCreateNotif.php +++ b/app/Listeners/BusinessUserCreateNotif.php @@ -35,22 +35,34 @@ class BusinessUserCreateNotif { $payload = $event->message; if ($payload->data->original->level === enum('levels.inactive.id')) { + // When user level in business is zero, probably user added to business by system + // And not necessary send notification to stockholders return; } $new_user = User::findOrFail($payload->data->original->user_id); $owners = Business::findOrFail($payload->business)->owners()->where('id', '!=', $new_user->id)->get(); - $notif = [ - 'greeting' => $this->getMessageLine($payload, 'greeting'), - 'subject' => $this->getMessageLine($payload, 'subject'), - 'title' => $this->getMessageLine($payload, 'title'), - 'body' => $this->getMessageLine($payload, 'body', ['business' => request('_business_info')['name'], 'user' => $new_user->name]) - ]; + $notif = $this->makeNotif($payload,['business' => request('_business_info')['name'], 'user' => $new_user->name]); $users = $owners->prepend($new_user); - Notification::send($users, new MailNotification($notif)); - Notification::send($users, new DBNotification($notif)); - Notification::send($users, new FcmNotification($notif)); + + $this->sendNotifications($users, $notif); + } + + /** + * Make notification object + * + * @param $payload + * @param array $options + * @return array + */ + public function makeNotif($payload, $options = []) { + return [ + 'greeting' => $this->getMessageLine($payload, 'greeting'), + 'subject' => $this->getMessageLine($payload, 'subject'), + 'title' => $this->getMessageLine($payload, 'title'), + 'body' => $this->getMessageLine($payload, 'body', $options) + ]; } /** @@ -66,4 +78,16 @@ class BusinessUserCreateNotif { return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options); } + + /** + * Call notifications + * + * @param $users + * @param $notif + */ + public function sendNotifications($users, $notif) { + Notification::send($users, new MailNotification($notif)); + Notification::send($users, new DBNotification($notif)); + Notification::send($users, new FcmNotification($notif)); + } } diff --git a/app/Listeners/ProjectUserCreateNotif.php b/app/Listeners/ProjectUserCreateNotif.php index d57a2c9..add26cb 100644 --- a/app/Listeners/ProjectUserCreateNotif.php +++ b/app/Listeners/ProjectUserCreateNotif.php @@ -49,6 +49,13 @@ class ProjectUserCreateNotif $this->sendNotifications($users, $notif); } + /** + * Make notification object + * + * @param $payload + * @param array $options + * @return array + */ public function makeNotif($payload, $options = []) { return [ 'greeting' => $this->getMessageLine($payload, 'greeting'), From c2064f96fc3273a628b1ccf1354408934f80392f Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 9 Mar 2021 14:30:12 +0330 Subject: [PATCH 49/51] add task create notification --- app/Events/TaskCreate.php | 28 +++++++ app/Listeners/TaskCreateNotif.php | 104 +++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 5 ++ resources/lang/fa/notification.php | 37 +++++++++ 4 files changed, 174 insertions(+) create mode 100644 app/Events/TaskCreate.php create mode 100644 app/Listeners/TaskCreateNotif.php diff --git a/app/Events/TaskCreate.php b/app/Events/TaskCreate.php new file mode 100644 index 0000000..e51dff2 --- /dev/null +++ b/app/Events/TaskCreate.php @@ -0,0 +1,28 @@ +message = $message; + } +} diff --git a/app/Listeners/TaskCreateNotif.php b/app/Listeners/TaskCreateNotif.php new file mode 100644 index 0000000..3bba2d8 --- /dev/null +++ b/app/Listeners/TaskCreateNotif.php @@ -0,0 +1,104 @@ +message; + + if ($payload->data->original->assignee_id !== null) { + $this->assigneeNotifHandler($payload); + } + if ($payload->data->original->approver_id !== null) { + $this->approverNotifHandler($payload); + } + } + + public function assigneeNotifHandler($payload) { + $user = User::findOrFail($payload->data->original->assignee_id); + $task = Task::findOrFail($payload->data->original->id); + + $notif = $this->makeNotif($payload, 'assignee', ['task' => $task->title]); + + $this->sendNotifications($user, $notif); + } + + public function approverNotifHandler($payload) { + $user = User::findOrFail($payload->data->original->approver_id); + $task = Task::findOrFail($payload->data->original->id); + + $notif = $this->makeNotif($payload, 'approver', ['task' => $task->title]); + + $this->sendNotifications($user, $notif); + } + + /** + * Make notification object + * + * @param $payload + * @param null $route + * @param array $options + * @return array + */ + public function makeNotif($payload, $route = null, $options = []) { + $route = $route == null ? "" : $route.'.'; + return [ + 'greeting' => $this->getMessageLine($payload, $route.'greeting'), + 'subject' => $this->getMessageLine($payload, $route.'subject'), + 'title' => $this->getMessageLine($payload, $route.'title'), + 'body' => $this->getMessageLine($payload, $route.'body', $options) + ]; + } + + /** + * Fetch message from notifications lang file + * + * @param $payload + * @param $key + * @param null $options + * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null + * + */ + public function getMessageLine($payload, $key, $options = []) + { + return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options); + } + + /** + * Call notifications + * + * @param $users + * @param $notif + */ + public function sendNotifications($users, $notif) { + Notification::send($users, new MailNotification($notif)); + Notification::send($users, new DBNotification($notif)); + Notification::send($users, new FcmNotification($notif)); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index de6d034..ea8a6b4 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -6,10 +6,12 @@ use App\Events\ProjectUserCreate; use App\Events\TagCreate; use App\Events\ModelSaved; use App\Events\BusinessUpdate; +use App\Events\TaskCreate; use App\Listeners\NotifHandler; use App\Listeners\ProjectUserCreateNotif; use App\Listeners\TagCreateNotif; use App\Events\BusinessUserCreate; +use App\Listeners\TaskCreateNotif; use Illuminate\Auth\Events\Registered; use App\Listeners\ActivityRegistration; use App\Listeners\BusinessUpdateListener; @@ -44,6 +46,9 @@ class EventServiceProvider extends ServiceProvider BusinessUpdate::class => [ BusinessUpdateListener::class, ], + TaskCreate::class => [ + TaskCreateNotif::class, + ], ]; /** diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index 1ef6303..12f9d99 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -35,6 +35,43 @@ return [ ] ], + 'tasks' => [ + 'create' => [ + 'assignee' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'تسک :task به شما واگذار شد.' + ], + 'approver' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'شما تایید کننده تسک :task شدید.' + ] + ], + 'update' => [ + 'assignee' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'تسک :task به شما واگذار شد.' + ], + 'approver' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'شما تایید کننده تسک :task شدید.' + ], + 'watcher' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'شما تایید کننده تسک :task شدید.' + ] + ] + ], + 'business' => [ 'update' => 'کاربر :user به کسب و کار :business اضافه شد.', 'update_wallet' => 'پول رو میدی یا پولت کنم', From 70a7c28f412cbfd1a8c74d7369207444e06b84c7 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 9 Mar 2021 18:27:55 +0330 Subject: [PATCH 50/51] add task update notification: ready_to_test, completed_at, assignee, approver --- app/Events/TaskUpdate.php | 28 +++++ app/Listeners/TaskUpdateNotif.php | 157 +++++++++++++++++++++++++ app/Providers/EventServiceProvider.php | 5 + resources/lang/fa/notification.php | 10 +- 4 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 app/Events/TaskUpdate.php create mode 100644 app/Listeners/TaskUpdateNotif.php diff --git a/app/Events/TaskUpdate.php b/app/Events/TaskUpdate.php new file mode 100644 index 0000000..91e18bd --- /dev/null +++ b/app/Events/TaskUpdate.php @@ -0,0 +1,28 @@ +message = $message; + } +} diff --git a/app/Listeners/TaskUpdateNotif.php b/app/Listeners/TaskUpdateNotif.php new file mode 100644 index 0000000..65981a2 --- /dev/null +++ b/app/Listeners/TaskUpdateNotif.php @@ -0,0 +1,157 @@ +message; + if (isset($payload->data->diff->assignee_id)) { + $this->assigneeNotifHandler($payload); + } + if (isset($payload->data->diff->approver_id)) { + $this->approverNotifHandler($payload); + } + if (isset($payload->data->diff->completed_at)) { + $this->completedNotifHandler($payload); + } + if (isset($payload->data->diff->ready_to_test)) { + $this->readyNotifHandler($payload); + } + } + + public function assigneeNotifHandler($payload) { + $user = User::findOrFail($payload->data->diff->assignee_id); + $task = Task::findOrFail($payload->data->original->id); + + $notif = $this->makeNotif($payload, 'assignee', ['task' => $task->title]); + + $this->sendNotifications($user, $notif); + } + + public function approverNotifHandler($payload) { + $user = User::findOrFail($payload->data->diff->approver_id); + $task = Task::findOrFail($payload->data->original->id); + + $notif = $this->makeNotif($payload, 'approver', ['task' => $task->title]); + + $this->sendNotifications($user, $notif); + } + + public function completedNotifHandler($payload){ + $task = Task::findOrFail($payload->data->original->id); + $user_ids = array_filter([ + $task->approver_id, + $task->assignee_id, + $task->creator_id + ]); + $user_ids = array_unique(array_merge($user_ids, $task->watchers)); + + $users = User::whereIn('id', $user_ids)->where('id', '!=', auth()->id())->get(); + + $notif = $this->makeNotif($payload, 'completed', ['task' => $task->title]); + + $this->sendNotifications($users, $notif); + } + + public function readyNotifHandler($payload) + { + $task = Task::findOrFail($payload->data->original->id); + $user_ids = array_unique(array_filter([ + $task->approver_id, + $task->assignee_id, + $task->creator_id + ])); + + $users = User::whereIn('id', $user_ids)->where('id', '!=', auth()->id())->get(); + + $notif = $this->makeNotif($payload, 'ready', ['task' => $task->title]); + + $this->sendNotifications($users, $notif); + } + + /** + * Make notification object + * + * @param $payload + * @param null $route + * @param array $options + * @return array + */ + public function makeNotif($payload, $route = null, $options = []) { + $route = $route == null ? "" : $route.'.'; + return [ + 'greeting' => $this->getMessageLine($payload, $route.'greeting'), + 'subject' => $this->getMessageLine($payload, $route.'subject'), + 'title' => $this->getMessageLine($payload, $route.'title'), + 'body' => $this->getMessageLine($payload, $route.'body', $options) + ]; + } + + /** + * Fetch message from notifications lang file + * + * @param $payload + * @param $key + * @param null $options + * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null + * + */ + public function getMessageLine($payload, $key, $options = []) + { + return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options); + } + + /** + * Call notifications + * + * @param $users + * @param $notif + */ + public function sendNotifications($users, $notif) { +// switch ($level) { +// case "emergency": +//// Notification::send($users, new SmsNotification($notif)); +// case "critical": +// Notification::send($users, new MailNotification($notif)); +// case "high": +// Notification::send($users, new DBNotification($notif)); +// case "medium": +// Notification::send($users, new FcmNotification($notif)); +// case "low": +//// Notification::send($users, new SocketNotification($notif)); +// default: +//// Notification::send($users, new SocketNotification($notif)); +// } + Notification::send($users, new MailNotification($notif)); + Notification::send($users, new DBNotification($notif)); + Notification::send($users, new FcmNotification($notif)); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index ea8a6b4..cdd86b4 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -7,11 +7,13 @@ use App\Events\TagCreate; use App\Events\ModelSaved; use App\Events\BusinessUpdate; use App\Events\TaskCreate; +use App\Events\TaskUpdate; use App\Listeners\NotifHandler; use App\Listeners\ProjectUserCreateNotif; use App\Listeners\TagCreateNotif; use App\Events\BusinessUserCreate; use App\Listeners\TaskCreateNotif; +use App\Listeners\TaskUpdateNotif; use Illuminate\Auth\Events\Registered; use App\Listeners\ActivityRegistration; use App\Listeners\BusinessUpdateListener; @@ -49,6 +51,9 @@ class EventServiceProvider extends ServiceProvider TaskCreate::class => [ TaskCreateNotif::class, ], + TaskUpdate::class => [ + TaskUpdateNotif::class, + ], ]; /** diff --git a/resources/lang/fa/notification.php b/resources/lang/fa/notification.php index 12f9d99..9e5b113 100644 --- a/resources/lang/fa/notification.php +++ b/resources/lang/fa/notification.php @@ -63,11 +63,17 @@ return [ 'title' => 'افزودن تسک جدید', 'body' => 'شما تایید کننده تسک :task شدید.' ], - 'watcher' => [ + 'ready' => [ 'greeting' => 'سلام کاربر گرامی!', 'subject' => 'افزودن تسک جدید', 'title' => 'افزودن تسک جدید', - 'body' => 'شما تایید کننده تسک :task شدید.' + 'body' => 'تسک :task در حالت انتظار برای تست قرار دارد.' + ], + 'completed' => [ + 'greeting' => 'سلام کاربر گرامی!', + 'subject' => 'افزودن تسک جدید', + 'title' => 'افزودن تسک جدید', + 'body' => 'تسک :task به اتمام رسید.' ] ] ], From b5ec3615ce1de5bdb3132b465d6f154b66a9a7a2 Mon Sep 17 00:00:00 2001 From: mahdihty Date: Tue, 9 Mar 2021 19:07:41 +0330 Subject: [PATCH 51/51] add notification helper class --- app/Listeners/TaskUpdateNotif.php | 2 - .../HelperClass/NotificationHelper.php | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 app/Utilities/HelperClass/NotificationHelper.php diff --git a/app/Listeners/TaskUpdateNotif.php b/app/Listeners/TaskUpdateNotif.php index 65981a2..118036c 100644 --- a/app/Listeners/TaskUpdateNotif.php +++ b/app/Listeners/TaskUpdateNotif.php @@ -8,8 +8,6 @@ use App\Models\User; use App\Notifications\DBNotification; use App\Notifications\FcmNotification; use App\Notifications\MailNotification; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Facades\Notification; class TaskUpdateNotif diff --git a/app/Utilities/HelperClass/NotificationHelper.php b/app/Utilities/HelperClass/NotificationHelper.php new file mode 100644 index 0000000..4231cbe --- /dev/null +++ b/app/Utilities/HelperClass/NotificationHelper.php @@ -0,0 +1,72 @@ + $this->getMessageLine($payload, $route.'greeting'), + 'subject' => $this->getMessageLine($payload, $route.'subject'), + 'title' => $this->getMessageLine($payload, $route.'title'), + 'body' => $this->getMessageLine($payload, $route.'body', $options) + ]; + } + + /** + * Fetch message from notifications lang file + * + * @param $payload + * @param $key + * @param null $options + * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Translation\Translator|string|null + * + */ + public function getMessageLine($payload, $key, $options = []) + { + return __('notification.'.$payload->data->table_name.'.'.enum('cruds.inverse.'.$payload->data->crud_id.'.singular_name').'.'.$key, $options); + } + + /** + * Call notifications + * + * @param $users + * @param $notif + */ + public function sendNotifications($users, $notif) { +// switch ($level) { +// case "emergency": +//// Notification::send($users, new SmsNotification($notif)); +// case "critical": +// Notification::send($users, new MailNotification($notif)); +// case "high": +// Notification::send($users, new DBNotification($notif)); +// case "medium": +// Notification::send($users, new FcmNotification($notif)); +// case "low": +//// Notification::send($users, new SocketNotification($notif)); +// default: +//// Notification::send($users, new SocketNotification($notif)); +// } + Notification::send($users, new MailNotification($notif)); + Notification::send($users, new DBNotification($notif)); + Notification::send($users, new FcmNotification($notif)); + } + +}