Compare commits

..
388 Commits
Author SHA1 Message Date
Giovanni 7299a177a2 fix wrong namespace use 2026-10-09 21:25:46 +02:00
Giovanni 67f2b516e8 fix wrong namespace use 2026-10-09 21:15:00 +02:00
Giovanni 64d065caad fix wrong namespace use 2026-10-09 21:04:15 +02:00
Giovanni ef0596b729 fix namespace and use spelling mistake 2026-10-09 20:42:34 +02:00
Giovanni b8420f08cd fix wrong namespace use 2026-10-09 20:09:01 +02:00
Giovanni e7baf34c4f fix wrong namespace use 2026-10-09 18:45:22 +02:00
Giovanni 7b4512bf27 fix wrong namespace use 2026-10-09 18:33:35 +02:00
Giovanni eb3e00c9ce remove unnecessary code 2026-10-04 13:04:33 +02:00
Giovanni 8e481dfae6 remove unnecessary code 2026-10-04 00:53:19 +02:00
Giovanni 23e61c6349 remove unnecessary code 2026-10-04 00:52:11 +02:00
Giovanni 74c5d2d5ce remove unnecessary code 2026-10-04 00:43:46 +02:00
Giovanni 62e2420105 remove unnecessary code 2026-10-04 00:36:38 +02:00
Giovanni c4ad3e0cdf remove unnecessary code 2026-10-04 00:29:05 +02:00
Giovanni 3d06596fea remove unnecessary code 2026-10-04 00:18:21 +02:00
Giovanni 30987fb582 remove unnecessary code 2026-10-03 23:01:08 +02:00
Giovanni 75bf1b966b remove unnecessary code 2026-10-03 22:58:42 +02:00
Giovanni 99f483b89c remove unnecessary code 2026-10-03 22:56:03 +02:00
Giovanni 58b471607c remove unnecessary code 2026-10-03 22:43:50 +02:00
Giovanni fc765959f9 remove unnecessary code 2026-10-03 22:35:57 +02:00
Giovanni 3d70814520 remove unnecessary code 2026-10-03 22:24:13 +02:00
Giovanni 840808daee remove unnecessary code 2026-10-03 22:03:22 +02:00
Giovanni 9619ce44e9 remove unnecessary code 2026-10-03 21:48:08 +02:00
Giovanni d469f1b5c8 remove unnecessary code 2026-10-03 21:41:04 +02:00
Giovanni 7f8db6d6ee remove unnecessary code 2026-10-03 21:28:50 +02:00
Giovanni 90dea59176 remove unnecessary code 2026-10-03 21:19:00 +02:00
Giovanni 9dd9f245f7 remove unnecessary code 2026-10-03 21:11:13 +02:00
Giovanni 02083d6ad3 remove unnecessary code 2026-10-03 21:01:09 +02:00
Giovanni fbbb563654 remove unnecessary code 2026-10-03 20:08:35 +02:00
Giovanni aa8663a416 remove unnecessary code 2026-10-03 20:06:33 +02:00
Giovanni 3feb126f07 remove unnecessary code 2026-10-03 20:02:35 +02:00
Giovanni 0ca1bb7e55 remove unnecessary code 2026-10-03 19:57:43 +02:00
Giovanni 7f4493666e remove unnecessary code 2026-10-03 19:31:13 +02:00
Giovanni 6cf9c8c4b2 remove unnecessary code 2026-10-03 19:12:58 +02:00
Giovanni ed38086d09 try to implement the global containerization 2026-10-03 18:42:21 +02:00
Giovanni e2b471312c refactor: move backend in apps/backend 2026-10-03 18:35:36 +02:00
Giovanni cf529278bd Merge backend history 2026-10-03 18:28:20 +02:00
Giovanni 31304c931d refactor: move frontend in apps/frontend 2026-10-03 18:26:34 +02:00
Giovanni dc39113335 add to my own gitea 2026-07-15 18:52:21 +02:00
Giovanni fed6b7c92f add to my own Gitea 2026-07-15 18:51:43 +02:00
Giovanni 7a4c2f5015 fix spelling mistake 2026-04-02 10:11:34 +02:00
Giovanni 76f7ed4f8d fix checkbox css 2026-04-02 00:22:04 +02:00
Giovanni c210aec76f adapt pictures for EventCards 2026-04-01 23:52:56 +02:00
Giovanni f1798c496b Merge branch 'features/header' into dev 2026-04-01 23:35:38 +02:00
Giovanni 26bfdb5cb2 Merge branch 'features/eventPhoto' into dev 2026-04-01 23:33:47 +02:00
Giovanni 6bce33e9de add eventPicture 2026-04-01 23:31:12 +02:00
Giovanni 9ae484d584 add methods for EventPicture' 2026-04-01 23:30:17 +02:00
p2405951 089995c84d add ProfilePicture into header when user login 2026-04-01 21:24:37 +02:00
Giovanni 615c084995 add event_photo_path attribut to Event model 2026-04-01 20:15:13 +02:00
Giovanni afbffe99a8 add event_photo_path attribut to create_events_table migration 2026-04-01 20:15:06 +02:00
ORDONNEAU Antoine 476f4390fe Merge branch 'dev' into fixcss/VolunteersPage 2026-04-01 18:20:25 +02:00
ORDONNEAU Antoine 483e281acd fixcss de la page Vonlontaire 2026-04-01 18:17:59 +02:00
Giovanni 0c0af0fd00 use vite_api_url in requestEmailChange link 2026-03-28 19:23:19 +01:00
Giovanni d9ccd7ff5f add vite_api_url in app variable 2026-03-28 19:21:07 +01:00
Giovanni 741dabd720 create confirmEmailChange route in User route 2026-03-28 18:53:54 +01:00
Giovanni f4caf50f40 create confirmEmailChange method in User controller 2026-03-28 18:53:43 +01:00
Giovanni 8e110244bb create requestEmailChange route in User route 2026-03-28 18:53:21 +01:00
Giovanni 567bc306e1 create requestEmailChange method in User controller 2026-03-28 18:52:50 +01:00
Giovanni 1f42a5392b Add email to new address on email change request apply 2026-03-28 18:52:07 +01:00
Giovanni ac2dcef7a7 Add email to new address on email change request 2026-03-28 18:51:53 +01:00
Giovanni 7b4280d4eb Add email to old address on email change request 2026-03-28 18:51:42 +01:00
Giovanni 4031faf08b use updatePassword mail 2026-03-28 17:58:58 +01:00
Giovanni 59598e29be create updatePassword mail 2026-03-28 17:57:57 +01:00
Giovanni 51435f9257 create updatePassword.blade 2026-03-28 17:57:50 +01:00
Giovanni 2489aad0c8 create updatePassword route in User route 2026-03-28 17:55:04 +01:00
Giovanni f52d5bb5ef create updatePassword method in User controller 2026-03-28 17:54:36 +01:00
Giovanni f0070d3557 update default admin password and email 2026-03-28 17:14:10 +01:00
Giovanni cbe816fd17 use uuid instead of id to delete notifications 2026-03-28 17:02:00 +01:00
Giovanni cdc4645d92 fix typesense error to delete unexisting user 2026-03-28 16:45:57 +01:00
Giovanni 60760d6101 use native Laravel notification methods 2026-03-28 16:41:38 +01:00
Giovanni 4c52fa5043 remove Mail use from Notifications classes 2026-03-28 16:37:02 +01:00
Giovanni f13e96c7c7 use the right Notification Facades 2026-03-28 16:23:01 +01:00
Giovanni aa3d5f887d use native Notification table and migration 2026-03-28 16:13:46 +01:00
Giovanni 93bfc19e6e use native Notification table and migration 2026-03-28 16:03:18 +01:00
Giovanni de0457c426 update channels route to use native laravel notifications 2026-03-28 15:52:37 +01:00
Giovanni ef33591c2a use EventDeleted notification 2026-03-28 15:50:17 +01:00
Giovanni 551909aee7 create EventDeleted notification 2026-03-28 15:50:13 +01:00
Giovanni bfec1752e0 delete EventParticipationCancelled event 2026-03-28 15:49:58 +01:00
Giovanni 0b3e9142b5 remove spaces 2026-03-28 15:48:34 +01:00
Giovanni a99a610688 remove spaces 2026-03-28 15:47:25 +01:00
Giovanni 4a0087104c use VolunteerUnassignedFromTask notification 2026-03-28 15:43:50 +01:00
Giovanni 52c187dc2c create VolunteerUnassignedFromTask notification 2026-03-28 15:43:45 +01:00
Giovanni f09b396826 delete VolunteerUnassignedFromTask event 2026-03-28 15:43:35 +01:00
Giovanni 094b25abdd use TaskDateUpdated notification 2026-03-28 15:40:57 +01:00
Giovanni 3e5dc3f377 use TaskDateUpdated notification 2026-03-28 15:40:26 +01:00
Giovanni 0a9b4874db create TaskDateUpdated notification 2026-03-28 15:40:22 +01:00
Giovanni 33d9edc39f delete TaskDateUpdated event 2026-03-28 15:40:12 +01:00
Giovanni 3789a2107a use TaskDeleted notification 2026-03-28 15:37:14 +01:00
Giovanni 8c8fb946e1 create TaskDeleted notification 2026-03-28 15:37:10 +01:00
Giovanni 165a24f5d0 delete TaskParticipationCancelled event 2026-03-28 15:36:54 +01:00
Giovanni 8df5d54d14 use VolunteerAssignedToTask notification 2026-03-28 15:34:36 +01:00
Giovanni 0ffd8e8ad4 create VolunteerAssignedToTask notification 2026-03-28 15:34:31 +01:00
Giovanni b9283136ba remove VolunteerAssignedToTask event 2026-03-28 15:34:16 +01:00
Giovanni f31b92e0d3 use VolunteerRoleUpdated notification 2026-03-28 15:31:38 +01:00
Giovanni 42879b9d76 create VolunteerRoleUpdated notification 2026-03-28 15:31:34 +01:00
Giovanni 17fa90ab9d remove VolunteerRoleUpdated event 2026-03-28 15:31:10 +01:00
Giovanni 2374774577 create UserRegistered notification 2026-03-28 15:28:33 +01:00
Giovanni 7e6052d879 use UserRegistered notification 2026-03-28 15:27:30 +01:00
Giovanni faa642f5d9 create UserRegistered notification 2026-03-28 15:27:26 +01:00
Giovanni d4092a5a3e remove UserCreated event 2026-03-28 15:27:06 +01:00
ORDONNEAU Antoine 9b814151ff changement des couleurs des progressBar pour les tasks 2026-03-27 17:25:24 +01:00
ORDONNEAU Antoine ee279cc9c2 optimisation du css 2026-03-27 17:12:43 +01:00
T'JAMPENS QUENTIN p2406187 5de0daa54a Send users_count on event task 2026-03-27 16:28:34 +01:00
T'JAMPENS QUENTIN p2406187 94beb74242 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/Http/Controllers/EventsController.php
2026-03-27 14:50:40 +01:00
T'JAMPENS QUENTIN p2406187 ab36d90796 Send users_count on event task 2026-03-27 14:49:44 +01:00
Giovanni 1d9c6726b3 check if user accept notifications 2026-03-26 10:29:51 +01:00
Giovanni 83ec6c9a2a correct wrong spelling of web_notifications 2026-03-26 10:19:18 +01:00
Giovanni c73f4bd35b correct wrong ternary condition 2026-03-26 09:57:47 +01:00
Giovanni 3528df0571 fix toggleWebNotifications method name 2026-03-26 09:54:32 +01:00
Giovanni 368465b8f3 add default value to email_notifications and web_notifications 2026-03-26 09:48:36 +01:00
Giovanni fdc79baf26 create /users/{id}/web-notifications route in user route 2026-03-26 09:15:21 +01:00
Giovanni fa58184f21 create toggleWebNotifications method in user controller 2026-03-26 09:14:55 +01:00
Giovanni 78645e3cb0 create /users/{id}/email-notifications route in user route 2026-03-26 09:14:17 +01:00
Giovanni 712bdd02cd create toggleEmailNotifications method in user controller 2026-03-26 09:13:57 +01:00
Giovanni c1d7f5cd81 add web_notifications and email_notifications value to the created user in create method of user controller 2026-03-26 09:06:13 +01:00
Giovanni 32607f32d8 add web_notifications and email_notifications value to admin user in seeders 2026-03-26 09:05:40 +01:00
Giovanni 1f6f280958 add web_notifications and email_notifications attributs to User migration and model 2026-03-26 09:03:51 +01:00
JOSSERAND GIOVANNI p2405212 3457ac8282 Merge branch 'main' into 'dev'
create notification, broadcast notification and mail when a futur or ongoing...

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!30
2026-03-26 07:52:08 +00:00
T'JAMPENS QUENTIN p2406187 7366c2a199 Merge branch 'doc/tests' into dev 2026-03-25 14:32:10 +01:00
T'JAMPENS QUENTIN p2406187 12440232da Updagre tests 2026-03-25 14:31:54 +01:00
T'JAMPENS QUENTIN p2406187 1aa367941e Merge branch 'dev' into 'main'
create notification, broadcast notification and mail when a futur or ongoing...

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!29
2026-03-25 13:10:59 +00:00
Giovanni 2252ac9547 add $notificationId to every broadcast notification 2026-03-19 18:22:36 +01:00
Giovanni 9fbc09017b add $notificationId to every broadcast notification 2026-03-19 18:09:11 +01:00
Giovanni ec76ca981a add information in notifications 2026-03-19 18:01:55 +01:00
Giovanni b7ccc38b68 add information in task date updated notification 2026-03-19 17:51:18 +01:00
Giovanni d627afdeaf add information in task date updated notification 2026-03-19 17:49:41 +01:00
Giovanni cf2a29a863 rename $task->title into $task->name 2026-03-19 17:43:02 +01:00
Giovanni 8371f96388 add event name in mail 2026-03-19 17:38:06 +01:00
Giovanni 6a825db608 create notification, broadcast notification and mail when a futur or ongoing date of a task is updated 2026-03-19 17:37:55 +01:00
Giovanni 16ae03e6e6 create broadcast notification when the user role is updated 2026-03-19 17:11:34 +01:00
Giovanni bda5aa4d50 remove debug Log::info 2026-03-19 17:02:27 +01:00
Giovanni 4ebb1ad6c0 fix attribut name 2026-03-19 10:22:59 +01:00
Giovanni 0e5d6a1270 merge dev into features/notifications 2026-03-19 10:00:18 +01:00
Giovanni 75d80985ff Merge branch dev into features/notifications 2026-03-19 09:56:35 +01:00
Giovanni ad6a0f6e90 check if the task is future or ongoing to send email and notification on unassign user 2026-03-19 08:44:12 +01:00
Giovanni 5d256637ec check if the task is future or ongoing to send email and notification on assign user 2026-03-19 08:43:53 +01:00
Giovanni 77d3efc555 check if the task is future or ongoing to send email and notification on deletion 2026-03-19 08:42:20 +01:00
Giovanni 6966027096 check if the event is future or ongoing to send email and notification on deletion 2026-03-19 08:41:07 +01:00
Giovanni 400d70ff7e use formatDate method 2026-03-18 16:30:27 +01:00
Giovanni 2ca3100b3c create a formatDate service and method 2026-03-18 16:30:13 +01:00
Giovanni 6c679596bd add date to eventParticipationCancelled mail 2026-03-18 16:18:12 +01:00
Giovanni 92de307017 create a mail when a user is unassigned to a task by an administrator 2026-03-18 16:14:35 +01:00
Giovanni 2921cbdcbd create a mail when a user is assigned to a task by an administrator 2026-03-18 11:48:54 +01:00
Giovanni 0c04694c34 fix spelling in taskParticipationCancelled blade 2026-03-18 11:44:23 +01:00
Giovanni 3d8fd6687d create a mail when a task is deleted to the participants 2026-03-18 11:37:47 +01:00
Giovanni ce887d0846 fix broadcast error with event 2026-03-18 11:33:42 +01:00
Giovanni 0057972c2f remove unecessary use 2026-03-18 11:07:58 +01:00
Giovanni da3bc1f534 Merge branch 'dev' into features/mails 2026-03-18 10:58:06 +01:00
Giovanni 62cb78859c improve deactivate account mail 2026-03-18 10:17:24 +01:00
Giovanni 5d98678d86 improve register mail 2026-03-18 10:13:17 +01:00
Giovanni 431b9eb0bb improve delete account mail 2026-03-18 10:11:56 +01:00
Giovanni f3d6f6bd11 create a mail when an event is deleted to the participants 2026-03-18 10:06:12 +01:00
Giovanni 2271304044 create a mail when a user registered 2026-03-18 10:00:47 +01:00
Giovanni 66266d1b4b remove notification deletion code when validating an account 2026-03-18 09:55:10 +01:00
Giovanni e713cc2857 create a mail when the role of the user is updated 2026-03-18 09:53:54 +01:00
Giovanni 96973f7884 create a mail when the role of the user is updated 2026-03-18 09:53:42 +01:00
Giovanni c3fb0ec094 create a mail when an account is deactivated by an administrator 2026-03-18 09:48:20 +01:00
Giovanni d9ce927d88 create a mail when an account is deleted by an administrator or by the user 2026-03-18 09:45:55 +01:00
Giovanni 870151cdd3 create a mail when an account is created by an administrator 2026-03-18 09:41:54 +01:00
Giovanni 7ff2f6a6d0 improve validate mail 2026-03-18 09:40:43 +01:00
Giovanni 6ff9d3a6a7 remove test email 2026-03-18 09:32:36 +01:00
T'JAMPENS QUENTIN p2406187 138077fa75 Merge branch 'features/admin-inscription' into 'dev'
Features/admin inscription

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!28
2026-03-15 15:14:59 +00:00
T'JAMPENS QUENTIN p2406187 2789f0e1d9 remove condition 2026-03-15 16:13:13 +01:00
T'JAMPENS QUENTIN p2406187 39702cec9f fix cors 2026-03-15 16:12:55 +01:00
Giovanni ee949da604 Merge branch 'dev' into features/mails 2026-03-13 15:31:37 +01:00
Giovanni 922b4df8b5 rename model in singular 2026-03-13 15:30:54 +01:00
Giovanni 928d504b27 Merge remote-tracking branch 'origin/dev' into features/notifications 2026-03-12 15:52:54 +01:00
Giovanni a189ce98a0 call .env variables in cors.php 2026-03-12 15:52:07 +01:00
Giovanni 3d7c76496f remove unecessary delete notifications on validate user 2026-03-12 11:01:54 +01:00
Giovanni c16a9d1785 remove unecessary delete notifications on validate user 2026-03-12 11:01:44 +01:00
Giovanni d2dd44af4d update channel name into users.admin 2026-03-12 10:19:58 +01:00
p2405951 c9382136ba Merge branch 'features/profile' into dev 2026-03-12 09:53:58 +01:00
Giovanni bed8c79c0a create event on user validated 2026-03-12 09:40:29 +01:00
Giovanni 15cd88c461 rename privateChannel to users.admin 2026-03-12 09:40:14 +01:00
Giovanni a65dcdee3b add user_id property on notification 2026-03-12 09:39:36 +01:00
Giovanni 2dcc861317 create notification on delete event 2026-03-12 09:38:58 +01:00
Giovanni d51d6a9c3f create notification on delete task, assign user and unassign user 2026-03-12 09:38:25 +01:00
Giovanni 9986dfa956 add user_id attribut on Notifications table 2026-03-09 16:53:38 +01:00
Giovanni e720c6c5dd create and send an email on account validate 2026-03-08 22:12:12 +01:00
Giovanni a33b6354bf create a test mail 2026-03-08 22:11:46 +01:00
p2405951 e67db45548 fix bug d'affichage profile-photod 2026-03-08 18:55:28 +01:00
T'JAMPENS QUENTIN p2406187 ec61d5e721 Merge branch 'fix/phone-number-update' into 'dev'
format phone number on update user

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!27
2026-03-03 11:27:43 +00:00
T'JAMPENS QUENTIN p2406187 1cabe42d83 format phone number on update user 2026-03-03 12:25:19 +01:00
p2405951 dd63f5ddd0 fix UserService.php 2026-02-24 17:02:55 +01:00
p2405951 ffa77ad386 recup stash 2026-02-24 16:43:16 +01:00
Giovanni d77e3dc82b remove useless notification when admin create an account 2026-02-24 16:04:26 +01:00
Giovanni 2c4c1cf403 update cors.php 2026-02-24 15:45:33 +01:00
Giovanni 3b304959a9 except broadcasting/auth from csrf token 2026-02-14 10:42:30 +01:00
Giovanni f32dee8e58 add broadcasting/auth to paths in cors.php 2026-02-14 10:32:22 +01:00
Giovanni 8c0ff638c3 test firstOrCreate method 2026-02-14 10:11:25 +01:00
Giovanni 6c3a11388b set config variables for env variables 2026-02-12 18:31:11 +01:00
Giovanni ac1eaca0df set config variables for env variables 2026-02-12 18:27:06 +01:00
Giovanni 7768e7a9e3 set config variables for env variables 2026-02-12 18:07:23 +01:00
Giovanni d3d7292b80 re-create DatabaseSeeder 2026-02-11 20:02:34 +01:00
LEBLOND MALO p2405951 a85e843863 Merge branch 'features/export' into 'dev'
add in stash export

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!26
2026-02-11 15:57:54 +00:00
p2405951 59c8268232 add in stash export 2026-02-11 17:52:46 +01:00
JOSSERAND GIOVANNI p2405212 7351cd5c53 Merge branch 'features/adminInCollection' into 'dev'
Features/adminincollection

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!25
2026-02-11 15:54:34 +00:00
Giovanni 97ae8366c0 add env variables for admin informations 2026-02-11 16:36:51 +01:00
Giovanni 7782983fef add env variables for admin informations 2026-02-11 16:31:23 +01:00
Giovanni 6b155d70b3 delete DatabaseSeeder 2026-02-11 16:01:49 +01:00
Giovanni 24aaf8661c create DevSeeder 2026-02-11 16:01:18 +01:00
Giovanni 4d089d004c create ProdSeeder 2026-02-11 16:01:11 +01:00
Giovanni 281094fb7b create ProdSeeder 2026-02-11 15:59:54 +01:00
Giovanni 06f8fec1ab create DevSeeder 2026-02-11 15:59:49 +01:00
Giovanni e006cb1c5e add the admin user in users collection 2026-02-11 15:48:50 +01:00
T'JAMPENS QUENTIN p2406187 c74690f68b Add default role when user is created by an admin 2026-02-11 14:51:28 +01:00
T'JAMPENS QUENTIN p2406187 5e232fd0af Create other user route 2026-02-11 14:51:28 +01:00
Giovanni accbbe31d9 test .gitignore 2026-02-10 16:39:33 +01:00
JOSSERAND GIOVANNI p2405212 06844e4b81 Merge branch 'fix/typesense' into 'dev'
Fix/typesense

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!24
2026-02-10 15:36:16 +00:00
Giovanni b1996d153e test 2026-02-10 16:33:06 +01:00
Giovanni b9ed209e5d test 2026-02-10 15:31:12 +01:00
Giovanni ed5edefcc4 update AppServiceProvider to add Observer 2026-02-10 15:28:22 +01:00
Giovanni 64455066a8 update SearchController 2026-02-10 15:28:04 +01:00
Giovanni 4acb8ad001 add TypesenseService 2026-02-10 15:27:55 +01:00
Giovanni 05ff869183 add Observer 2026-02-10 15:27:46 +01:00
Giovanni 3a5a95cba9 call TYPESENSE_API_KEY in SearchController.php 2026-02-07 21:28:49 +01:00
JOSSERAND GIOVANNI p2405212 880032bc28 Merge branch 'features/fixDocker' into 'dev'
Features/fixdocker

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!23
2026-02-07 19:03:26 +00:00
Giovanni 24f0365513 test 2026-02-06 15:45:42 +01:00
Giovanni b256e3b7dc test 2026-02-06 15:34:55 +01:00
Giovanni f483dbe17e test 2026-02-03 17:23:50 +01:00
Giovanni 569b9df7f2 test 2026-02-03 17:22:10 +01:00
Giovanni ae5a159d31 test 2026-02-03 17:14:32 +01:00
Giovanni 7f64c80b77 test 2026-02-03 17:11:41 +01:00
Giovanni 591127b5b6 test 2026-02-03 17:02:15 +01:00
Giovanni 1aa57e3e3e test 2026-02-03 16:42:42 +01:00
T'JAMPENS QUENTIN p2406187 881e3b7775 Fix event middleware 2026-02-03 14:42:13 +01:00
Giovanni 8c1171846d test 2026-01-31 18:09:54 +01:00
Giovanni a64146e227 test 2026-01-31 17:56:34 +01:00
Giovanni 22e6f365b0 test 2026-01-31 17:37:23 +01:00
Giovanni fadaf6765c test 2026-01-31 17:26:20 +01:00
Giovanni 637a32314a test 2026-01-31 16:20:12 +01:00
Giovanni 59105c6d8a test 2026-01-31 16:13:28 +01:00
Giovanni 33a918d02b test 2026-01-31 16:02:37 +01:00
Giovanni de7a0a85f6 test 2026-01-31 15:25:16 +01:00
Giovanni 8f91f480eb test 2026-01-31 15:00:25 +01:00
Giovanni f66c8a8eb6 update filesystems to change dynamically the storage path 2026-01-30 23:41:21 +01:00
Giovanni d4526487ed test public 2026-01-30 17:34:43 +01:00
Giovanni 32341b0311 update .env.example 2026-01-27 14:36:43 +01:00
Giovanni 0f45a25b25 delete docker folder 2026-01-27 14:35:49 +01:00
T'JAMPENS QUENTIN p2406187 7099ad53c5 Fix event middleware 2026-01-27 14:20:24 +01:00
p2405951 0f3beba354 test 2026-01-19 09:02:05 +01:00
p2405951 260584bf89 backup ignore 2026-01-19 08:58:44 +01:00
p2405951 a8132e9f13 end 2026-01-15 09:21:58 +01:00
T'JAMPENS QUENTIN p2406187 ccc188a8c0 Merge branch 'dev' into 'main'
Dev

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!21
2026-01-14 21:44:35 +00:00
Giovanni 446bded5f9 update deactivate 2026-01-14 22:59:13 +01:00
JOSSERAND GIOVANNI p2405212 c644c05933 Merge branch 'features/permissions' into 'dev'
permissions review

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!20
2026-01-14 20:42:20 +00:00
Giovanni 53cca29b67 permissions review 2026-01-14 22:41:03 +01:00
T'JAMPENS QUENTIN p2406187 396519e5f9 Merge branch 'dev' into 'main'
add reverb container, unread attribute in notification pivot table and private...

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!19
2026-01-14 15:47:43 +00:00
T'JAMPENS QUENTIN p2406187 dd2a0bc333 Create get all roles method 2026-01-13 23:46:08 +01:00
T'JAMPENS QUENTIN p2406187 797887b23d Create get all roles route 2026-01-13 09:29:26 +01:00
T'JAMPENS QUENTIN p2406187 8def3ee391 Add isUserAssigned route 2026-01-12 15:52:42 +01:00
T'JAMPENS QUENTIN p2406187 e118debc6b Improve assignSelf function 2026-01-12 14:15:08 +01:00
Giovanni de13fa0b4a add TYPESENSE_API_KEY in .env.example 2026-01-12 10:04:32 +01:00
Giovanni bf6deadbc0 add policies for getInvalidUsers route 2026-01-12 09:55:19 +01:00
JOSSERAND GIOVANNI p2405212 46a4ac9c15 Merge branch 'features/notifications' into 'dev'
add reverb container, unread attribute in notification pivot table and private...

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!18
2026-01-11 22:22:26 +00:00
Giovanni 60012c63c3 merge dev into features/notifications 2026-01-11 23:09:57 +01:00
Giovanni c3122072da correct some issues 2026-01-11 20:10:39 +01:00
Giovanni ab1df4d692 Create unassignUser, unassignSelf methods and Bruno requests. Add call events when needed 2026-01-11 19:03:58 +01:00
Giovanni 234717b23c multiplex user notifications over a single private channel 2026-01-11 12:09:58 +01:00
Giovanni 9fd72c6ea4 multiplex user notifications over a single private channel 2026-01-11 12:06:27 +01:00
Giovanni 9b70b09173 rename admins-private to users.registration 2026-01-09 14:41:10 +01:00
p2405951 2b8487432f --allow_empty 2026-01-09 09:35:21 +01:00
p2405951 b4a5283547 fix bug && test 2026-01-09 09:23:56 +01:00
Giovanni 69d6a77ff1 untrack public/index.html 2026-01-08 14:59:03 +01:00
Giovanni 475a1c7052 set the notification getting system by created_at desc order 2026-01-08 14:51:21 +01:00
Giovanni c49a5e558f add readNotifications route and bruno's request 2026-01-08 14:10:17 +01:00
Giovanni 51791a3b34 add readNotifications route and bruno's request 2026-01-08 14:04:53 +01:00
Giovanni 2790026475 add reverb container, unread attribute in notification pivot table and private channel and event for new register 2026-01-07 16:50:51 +01:00
LEBLOND MALO p2405951 ed37808d33 Merge branch 'features/backup' into 'dev'
Features/backup

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!16
2026-01-07 15:33:33 +00:00
p2405951 f066aa88f0 Nettoyage des backups 2026-01-07 16:30:33 +01:00
p2405951 3451f75b37 recup modif dev 2026-01-07 16:04:12 +01:00
T'JAMPENS QUENTIN p2406187 be3f6ba5d4 Create getInvalidUser route 2026-01-05 09:48:20 +01:00
T'JAMPENS QUENTIN p2406187 516ab77d58 Create getInvalidUser route 2026-01-05 09:48:13 +01:00
Giovanni 6eaac7a745 rename UserPolicy to UsersPolicy 2025-12-17 17:32:03 +01:00
Giovanni ab9755ade5 rename UserPolicy to UsersPolicy 2025-12-17 17:31:54 +01:00
JOSSERAND GIOVANNI p2405212 e0504ea0c2 Merge branch 'features/authorizations' into 'features/notifications'
Features/authorizations

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!14
2025-12-17 16:19:31 +00:00
Giovanni 08459ac627 change notifications functions 2025-12-17 17:18:37 +01:00
Giovanni 08c51473a7 add TasksPolicy and EventsPolicy 2025-12-17 17:11:12 +01:00
Giovanni a0e95aca3e remove unecessary gates for users routes and add UserPolicy 2025-12-17 16:52:43 +01:00
Giovanni 4c3647fe30 add gates for users routes 2025-12-17 16:14:19 +01:00
T'JAMPENS QUENTIN p2406187 4c907c8ba5 Return 404 if user not found 2025-12-17 15:16:03 +01:00
Giovanni ae81344fe2 add notification when user role is changed 2025-12-17 14:46:33 +01:00
Giovanni 7390ae6116 create notification bruno request and move some routes 2025-12-17 14:32:01 +01:00
Giovanni 090db4205a search only validate users 2025-12-16 11:44:32 +01:00
Giovanni 543db51291 search only validate users 2025-12-16 11:44:12 +01:00
Giovanni 8e93a0aba2 delete notification and notification_user when validate 2025-12-16 10:11:08 +01:00
Giovanni eab9565c7d create notification when register 2025-12-15 16:45:51 +01:00
Giovanni dfb70cc5ce create notification_user table migration 2025-12-10 17:02:45 +01:00
JOSSERAND GIOVANNI p2405212 e89f00e2f9 Merge branch 'features/searchUser' into 'dev'
Features/searchuser

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!12
2025-12-10 15:52:16 +00:00
JOSSERAND GIOVANNI p2405212 9bc03eb46a Merge branch 'dev' into 'features/searchUser'
# Conflicts:
#   tests/Users/Create user.bru
#   tests/collection.bru
2025-12-10 15:52:04 +00:00
T'JAMPENS QUENTIN p2406187 3e43966357 Send error if email is already used 2025-12-10 15:09:34 +01:00
T'JAMPENS QUENTIN p2406187 b3cffe3008 Return isAdmin 2025-12-10 12:10:50 +01:00
T'JAMPENS QUENTIN p2406187 8e1e8e3b7c Merge branch 'dev' of https://iutbg-gitlab.iutbourg.univ-lyon1.fr/sae-but2/2025-26/gestion-benevoles-association/backend into dev 2025-12-10 11:42:25 +01:00
T'JAMPENS QUENTIN p2406187 639675c266 Update user role on validation 2025-12-10 11:41:27 +01:00
T'JAMPENS QUENTIN p2406187 de08e5396a Update create user test 2025-12-10 11:40:56 +01:00
Giovanni 669fa6aa16 add TYPESENSE_CONNECTION in .env.example 2025-12-10 11:25:48 +01:00
Giovanni ba7146cca7 test commit 2025-12-10 11:23:33 +01:00
T'JAMPENS QUENTIN p2406187 afb630d053 Merge branch 'features/emailValidation' into 'dev'
Check user email

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!10
2025-12-10 08:53:13 +00:00
T'JAMPENS QUENTIN p2406187 056f42b815 Check user email 2025-12-10 09:52:22 +01:00
Giovanni 8426755db3 add user search in SearchController, add start and end datetime in events table 2025-12-09 12:28:02 +01:00
T'JAMPENS QUENTIN p2406187 319f143327 Merge branch 'dev' into features/phoneValidation
# Conflicts:
#	composer.json
#	composer.lock
2025-12-09 12:27:19 +01:00
T'JAMPENS QUENTIN p2406187 04e45f3b83 Check user phone 2025-12-09 12:20:12 +01:00
T'JAMPENS QUENTIN p2406187 6ae4fd1cdd Install propaganistas/laravel-phone 2025-12-09 12:19:56 +01:00
JOSSERAND GIOVANNI p2405212 da20be4558 Merge branch 'features/typesense' into 'dev'
Features/typesense

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!8
2025-12-07 15:14:57 +00:00
JOSSERAND GIOVANNI p2405212 b8ccef3fb5 Merge branch 'dev' into 'features/typesense'
# Conflicts:
#   routes/events.php
2025-12-07 15:14:51 +00:00
Giovanni 2db242d43a Replace bind mount with Docker managed volume for Typesense 2025-12-07 16:13:03 +01:00
T'JAMPENS QUENTIN p2406187 ad3157807c Send task on get all events route 2025-12-07 01:08:18 +01:00
T'JAMPENS QUENTIN p2406187 efd87bf5cd Create UserService 2025-12-05 15:08:18 +01:00
T'JAMPENS QUENTIN p2406187 7bf6ffe2f8 Create get event route doc 2025-12-02 16:25:30 +01:00
T'JAMPENS QUENTIN p2406187 6a0125eeaf Create get event route 2025-12-02 16:25:18 +01:00
T'JAMPENS QUENTIN p2406187 897e92d8f0 Create get role fct 2025-12-02 16:12:48 +01:00
T'JAMPENS QUENTIN p2406187 238127bc16 Update /me route 2025-12-02 16:12:29 +01:00
T'JAMPENS QUENTIN p2406187 232804fb39 Update composer.json 2025-12-02 15:28:56 +01:00
Giovanni 20654b2eff add of Search events route 2025-11-29 20:11:06 +01:00
Giovanni a7b8de3205 add of typesense container, searchEvents route and SearchController and method 2025-11-29 20:10:09 +01:00
T'JAMPENS QUENTIN p2406187 b7cd27be7e Fix errors 2025-11-29 01:48:20 +01:00
T'JAMPENS QUENTIN p2406187 99655b0d84 Merge branch 'feature/docker' into 'dev'
Feature/docker

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!7
2025-11-28 07:52:16 +00:00
T'JAMPENS QUENTIN p2406187 6c4d03eb6b Update 2025-11-24 23:18:10 +01:00
T'JAMPENS QUENTIN p2406187 1f077cfdd2 Update README.md 2025-11-24 22:19:26 +01:00
T'JAMPENS QUENTIN p2406187 60d1ec1364 Update docker conf 2025-11-24 22:16:16 +01:00
T'JAMPENS QUENTIN p2406187 1eb75b5782 Merge branch 'feature/docker' into 'dev'
Feature/docker

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!6
2025-11-23 22:38:51 +00:00
T'JAMPENS QUENTIN p2406187 457dd6679b Update README.md 2025-11-23 23:37:33 +01:00
T'JAMPENS QUENTIN p2406187 bd4dceace6 Add sample website 2025-11-23 23:35:32 +01:00
T'JAMPENS QUENTIN p2406187 b1a0edb474 Create docker configuration 2025-11-23 23:35:24 +01:00
T'JAMPENS QUENTIN p2406187 4ae2905b48 Merge branch 'feature/tasks' into 'dev'
Feature/tasks

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!5
2025-11-21 23:39:59 +00:00
T'JAMPENS QUENTIN p2406187 59a5a4fde9 add delete event route 2025-11-22 00:37:59 +01:00
T'JAMPENS QUENTIN p2406187 c83b626b9a add get all events route 2025-11-22 00:33:51 +01:00
T'JAMPENS QUENTIN p2406187 9e271f8a3e add update event task route 2025-11-21 16:16:34 +01:00
T'JAMPENS QUENTIN p2406187 d6a8fd3010 add update event route 2025-11-21 16:08:04 +01:00
T'JAMPENS QUENTIN p2406187 d5f1c7f163 add delete event tasks route 2025-11-21 15:44:25 +01:00
T'JAMPENS QUENTIN p2406187 016017ab30 add get event tasks route 2025-11-21 12:07:23 +01:00
T'JAMPENS QUENTIN p2406187 23da5640c7 Merge branch 'feature/front' into 'dev'
Host frontend

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!4
2025-11-21 10:52:25 +00:00
T'JAMPENS QUENTIN p2406187 83a401bd15 Host frontend 2025-11-21 11:49:03 +01:00
T'JAMPENS QUENTIN p2406187 5e846a8896 Add get user task by id 2025-11-21 11:08:11 +01:00
T'JAMPENS QUENTIN p2406187 feef269015 Add get user by id tasks 2025-11-20 12:16:38 +01:00
T'JAMPENS QUENTIN p2406187 d142979e49 Split routes 2025-11-20 12:09:17 +01:00
T'JAMPENS QUENTIN p2406187 feb901c9e8 Add get user's tasks route 2025-11-20 11:23:56 +01:00
T'JAMPENS QUENTIN p2406187 fd65cfdc1c fix max participant reached 2025-11-20 11:23:21 +01:00
T'JAMPENS QUENTIN p2406187 2c606d4fe7 resolve conflits 2025-11-20 09:01:13 +01:00
T'JAMPENS QUENTIN p2406187 347522a429 Merge branch 'docs' into 'dev'
Docs

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!3
2025-11-17 21:52:15 +00:00
T'JAMPENS QUENTIN p2406187 fdc4819c2e Update README.md 2025-11-17 22:51:25 +01:00
T'JAMPENS QUENTIN p2406187 dea69e018e Remove vars values + move headers to collection headers 2025-11-17 22:40:08 +01:00
T'JAMPENS QUENTIN p2406187 67048d7f4b Add methods 2025-11-17 22:35:09 +01:00
T'JAMPENS QUENTIN p2406187 5b01f2e9f4 Update doc 2025-11-17 22:32:30 +01:00
T'JAMPENS QUENTIN p2406187 ad0f51c405 Add /me route 2025-11-16 01:53:46 +01:00
Giovanni 901c6ab68e creation of the create and get routes for Notifications 2025-11-14 17:41:01 +01:00
T'JAMPENS QUENTIN p2406187 827ade852e Create assign task to user route 2025-11-14 12:22:59 +01:00
T'JAMPENS QUENTIN p2406187 55f332d28e Create assign task route 2025-11-14 09:47:13 +01:00
T'JAMPENS QUENTIN p2406187 71fc82a0e7 Create relationship task_user 2025-11-14 09:46:57 +01:00
Giovanni 3c2128b9b7 creation of Notifications table and migration 2025-11-09 18:04:25 +01:00
T'JAMPENS QUENTIN p2406187 f353e0c964 Creation table tasks 2025-11-09 16:17:10 +01:00
T'JAMPENS QUENTIN p2406187 8341021ba5 Merge branch 'features/events' into 'dev'
Features/events

See merge request sae-but2/2025-26/gestion-benevoles-association/backend!2
2025-10-27 22:20:00 +00:00
Quentin T'JAMPENS 86b7996d75 Create events factory 2025-10-27 23:13:52 +01:00
Quentin T'JAMPENS 8e3f189e77 Fix dev url 2025-10-27 23:07:24 +01:00
Quentin T'JAMPENS 75e156ae07 Fix xsrf tokens 2025-10-27 23:07:19 +01:00
Quentin T'JAMPENS 294c3581f9 Set default password to 'password' 2025-10-27 23:06:33 +01:00
Quentin T'JAMPENS 41cf8a5a44 Create event route + doc 2025-10-27 23:06:17 +01:00
Quentin T'JAMPENS adfe2a9fe1 Create event table 2025-10-27 22:46:35 +01:00
Quentin T'JAMPENS 9cf93d93c7 Update factory and seed 2025-10-27 22:33:52 +01:00
T'JAMPENS QUENTIN p2406187 a3c418de46 Create environnement 2025-10-24 17:18:43 +02:00
T'JAMPENS QUENTIN p2406187 8932c90c8a Change return error 2025-10-24 11:33:53 +02:00
T'JAMPENS QUENTIN p2406187 b3222f0f46 Add get all users route 2025-10-24 11:32:16 +02:00
T'JAMPENS QUENTIN p2406187 47d433eecb Add verified_at column 2025-10-24 10:59:55 +02:00
T'JAMPENS QUENTIN p2406187 081cd66a8e Add delete user by id route 2025-10-24 10:02:06 +02:00
T'JAMPENS QUENTIN p2406187 2c94483cd9 Add role and validate in getUser route 2025-10-24 09:42:00 +02:00
T'JAMPENS QUENTIN p2406187 2cf024fe3e Update extractCSRF function 2025-10-24 09:39:54 +02:00
T'JAMPENS QUENTIN p2406187 219c71a99b Add modify user role route 2025-10-24 09:35:44 +02:00
T'JAMPENS QUENTIN p2406187 5787d5fac3 Add validate user route 2025-10-23 11:57:11 +02:00
T'JAMPENS QUENTIN p2406187 7add68c780 Add update user doc 2025-10-23 11:33:47 +02:00
T'JAMPENS QUENTIN p2406187 4fdf38827d Add update user route 2025-10-23 11:33:39 +02:00
T'JAMPENS QUENTIN p2406187 ded8e85c85 Add get user route 2025-10-21 22:18:15 +02:00
T'JAMPENS QUENTIN p2406187 0d926d4b95 Add delete user route 2025-10-21 22:02:53 +02:00
Dayamond fd8c47cd95 Add logout route test 2025-10-21 17:41:52 +02:00
Dayamond ab50743c01 Add logout route 2025-10-21 17:41:44 +02:00
Dayamond 5a41d2e028 Update asserts 2025-10-21 17:25:34 +02:00
Dayamond a7b9ce803b Add routes 2025-10-21 17:18:00 +02:00
Dayamond 42a98f3435 Update create user route 2025-10-21 16:48:18 +02:00
Dayamond c691f81ebb Update user table 2025-10-21 16:45:39 +02:00
Dayamond ef775c70fe rename routes 2025-10-21 16:14:34 +02:00
Dayamond a2e96cef5a change default port 2025-10-21 16:14:27 +02:00
Dayamond 56c9139d0c Remove useless imports 2025-10-21 16:14:20 +02:00
Dayamond 3c929f5c64 Change bruno collection name 2025-10-18 17:20:40 +02:00
Dayamond 223cfe086c Add Bruno tests 2025-10-18 17:17:35 +02:00
Dayamond 1c00dbdb85 Ajout de l'authentification 2025-10-18 16:52:01 +02:00
Dayamond 70fbc02918 Migrations 2025-10-14 22:38:01 +02:00
Dayamond 3d6597a246 Add cors 2025-10-14 22:37:49 +02:00
Dayamond eb7a5c7894 create user 2025-10-14 22:37:42 +02:00
Dayamond ed5376cb43 create user 2025-10-14 22:37:32 +02:00
Dayamond 0f8ce7deea Initial commit 2025-10-13 23:04:46 +02:00
421 changed files with 22933 additions and 217 deletions

No files matched your search

Executable
+26
View File
@@ -0,0 +1,26 @@
DB_CONNECTION=
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=
DB_ROOT_PASSWORD=
TYPESENSE_API_KEY=
#Mettre à 1 quand vous êtes en développement et à rien en production
DEV=
USER_ID=1000
GROUP_ID=1000
VITE_API_URL=http://localhost:80
VITE_BROADCASTER=reverb
VITE_REVERB_KEY=local
VITE_REVERB_HOST=127.0.0.1
VITE_REVERB_PORT=8080
VITE_FORCE_TLS=false
VITE_DISABLE_STATS=true
VITE_ENCRYPTED=false
VITE_CLUSTER=mt1
VITE_ENABLED_TRANSPORTS=['ws','wss']
Regular → Executable
+2 -28
View File
@@ -1,29 +1,3 @@
.env
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*storybook.log
storybook-static
.env
backups/
Regular → Executable
+196 -9
View File
@@ -1,16 +1,203 @@
# React + Vite
# Gestion des bénévoles - Comité des fêtes de Beaupont
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
## Présentation du projet
Currently, two official plugins are available:
L'association organise régulièrement des événements et projets mobilisant des bénévoles, mais sa coordination interne est rendue difficile par l'usage d'outils dispersés, et non communicants.
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
La création d'une plateforme web unique vise donc à centraliser et simplifier la gestion des utilisateurs, des rôles, des événements, des tâches et de la communication. Elle vise à améliorer la collaboration entre tous les bénévoles de l'association.
## React Compiler
## Objectifs de l’application
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
L’application a pour objectifs principaux de :
## Expanding the ESLint configuration
- Gérer les utilisateurs et leurs rôles au sein de l’association.
- Organiser et planifier les événements et les tâches associées.
- Permettre aux bénévoles de s’inscrire en ligne aux événements et aux tâches.
- Offrir aux responsables un tableau de bord pour suivre la participation et les besoins en ressources humaines.
- Centraliser les informations et la communication autour des projets associatifs.
If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
## Prérequis
Comme notre architecture se base sur l'utilisation de Docker, les seules prérequis pour la machine sur laquelle vous souhaitez installer cette application sont :
- [Docker](https://www.docker.com/)
- [Git](https://git-scm.com/)
## Installation
Pour installer ce projet il vous suffit de cloner le dépôt avec la commande suivante :
```bash
git clone <adresse du dépôt>
```
## Initialisation du projet
### 1. Configuration du fichier `.env` principal
1. Créez un fichier `.env` à la racine du projet.
2. Copiez le contenu du fichier `.env.example` dans le fichier `.env`.
3. Renseignez les variables suivantes :
| Variable | Description |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DB_CONNECTION` | Type de base de données utilisée (ex : `mysql`, `mariadb`) |
| `DB_HOST` | Adresse du serveur de base de données (ex : `localhost`, `db`) |
| `DB_PORT` | Port de la base de données (ex : `3306` pour MySQL/MariaDB) |
| `DB_DATABASE` | Nom de la base de données |
| `DB_USERNAME` | Nom d’utilisateur de la base de données |
| `DB_PASSWORD` | Mot de passe de l’utilisateur de la base de données |
| `DB_ROOT_PASSWORD` | Mot de passe du compte root de la base de données |
| `DEV` | Mettre `1` en environnement de développement, laisser vide sinon. |
| `VITE_API_URL` | URL du frontend hébergé et exposé par le conteneur Nginx (par défaut sur le port 80) |
### 2. Configuration du fichier `.env` php
Répétez la même opération pour le fichier `docker/php/.env` en copiant depuis `docker/php/.env.example`.
De plus, un utilisateur administrateur est automatiquement créé avec les informations suivantes :
- **Prénom :** `admin`
- **Nom :** `admin`
- **Adresse e-mail :** `admin@mail.com`
- **Mot de passe :** `admin`
- **Numéro de téléphone :** `0909090909`
> Il est bien sûr possible de modifier ces valeurs dans le fichier `docker/php/.env` avant le premier lancement du seeder, en utilisant les variables : `ADMIN_NAME`, `ADMIN_LASTNAME`, `ADMIN_MAIL`, `ADMIN_PASSWORD` et `ADMIN_PHONE_NUMBER`
### 3. Note importante
> ⚠️ Si vous êtes sous **Windows**, assurez-vous que tous les fichiers `.sh` utilisent des fins de ligne **LF** et non **CRLF**, sous peine d’erreurs lors de l’exécution des scripts.
## Lancement
Une fois la configuration terminée :
1. Ouvrez un terminal.
2. Placez-vous à la racine du projet.
3. Exécutez les commandes suivantes dans l'ordre :
```bash
docker compose down -v
```
> Arrête et supprime les conteneurs existants ainsi que les volumes associés.
> Cette étape permet de repartir sur une base propre.
```bash
docker compose up --build -d
```
> Reconstruit les images Docker et démarre l’ensemble des services en arrière-plan.
## Point technique
### 1. Le nommage
Nous avons adopté une convention de nommage en **camelCase** pour les variables, fonctions et fichiers JavaScript côté frontend.
Pour les composants React, nous utilisons la convention **PascalCase** afin de les différencier des éléments HTML.
Côté backend (Laravel), nous respectons les conventions PHP :
- **camelCase** pour les variables et méthodes
- **PascalCase** pour les classes (Controllers, Models, Policies, etc.)
Les noms sont choisis de manière explicite et descriptive afin de faciliter la compréhension du code et sa maintenance.
### 2. Les commentaires
Nous avons fait le choix de limiter l’utilisation des commentaires dans le code. En effet, nous privilégions un code clair, lisible et auto-documenté, notamment grâce à :
- un nommage explicite des variables, fonctions et classes
- l’utilisation du typage lorsque cela est possible
- une structure de projet organisée
- une documentation externe (README, Storybook, Bruno)
Les commentaires sont donc utilisés uniquement lorsque cela apporte une réelle valeur ajoutée, par exemple pour expliquer une logique complexe, un choix technique spécifique ou un comportement non évident.
### 3. La documentation
La documentation du projet est assurée à plusieurs niveaux :
- Un **README** structuré expliquant l’installation, la configuration et le fonctionnement global
- Une documentation des routes API via **Bruno**
- L’utilisation de **Storybook** pour documenter et tester visuellement les composants UI
### 4. La programmation défensive
Nous avons appliqué la programmation défensive afin de rendre notre application plus robuste, sécurisée et fiable face aux erreurs potentielles.
Dans notre projet, cela se traduit notamment par :
- Validation des données côté backend (Laravel) avant utilisation
- Vérification des entrées utilisateur afin d’éviter les données incohérentes ou malveillantes
- Gestion des rôles et permissions pour empêcher l’accès à des ressources non autorisées (utilisation de policies Laravel)
- Gestion des cas d’erreur (ex : utilisateur inexistant, tâche non trouvée, accès interdit) avec des réponses HTTP adaptées
- Utiliser des valeurs par défaut ou des vérifications avant utilisation pour éviter les erreurs inattendues
Enfin, grâce à Docker, nous limitons les problèmes liés à l’environnement (configuration, versions, dépendances), ce qui contribue également à une approche défensive globale du projet.
### 5. La gestion des erreurs
La gestion des erreurs est assurée à plusieurs niveaux :
- Backend Laravel : gestion des exceptions et retour de codes HTTP appropriés (200, 401, 404, 422, etc.)
- Frontend React : gestion des erreurs utilisateur (formulaires, affichage de messages d’erreur)
### 6. Le débogage
Le débogage a été réalisé grâce à plusieurs outils et méthodes :
- Utilisation des outils de développement du navigateur (console, réseau)
- Tests des routes API avec Bruno
- Logs côté docker et backend pour analyser le comportement du serveur
- Par exemple un membre de notre groupe rencontré quelques problèmes lors du lancement des conteneurs docker. Il a donc accédé aux logs du conteneur en question, qui indiquait un problème de clonage du repo git : `unable to access to {url du depot git}` (cela peut être du a un `Personal Access Token` expiré, ou une mauvaise connexion internet)
- Tests manuels réguliers lors du développement
### 7. L'architecture
Nous avons choisi d’adopter une architecture basée sur trois dépôts Git distincts, afin de séparer clairement les responsabilités et de faciliter la maintenance du projet :
- **Dépôt frontend** : contient le code de l’application développée en React
- **Dépôt backend** : contient le code de l’API développée avec Laravel
- **Dépôt Docker** : contient toute la configuration nécessaire à l’installation et au déploiement du projet sur n’importe quelle machine
Ce troisième dépôt permet de déployer l’ensemble de l’application de manière reproductible grâce à Docker, en centralisant la configuration des services.
Notre environnement de déploiement repose sur plusieurs conteneurs Docker :
- **Backend (Laravel)** : gestion de la logique métier et de l’API
- **Frontend (React)** : interface utilisateur (version temporaire pour le déploiement)
- **Base de données (MariaDB)** : stockage des données
- **Nginx** : serveur web utilisé pour exposer les services et gérer les requêtes HTTP
- **Typesense** : moteur de recherche utilisé pour améliorer les performances des recherches
- **Backup** : conteneur dédié à la sauvegarde de la base de données
- **Serveur WebSocket (Laravel Reverb)** : gestion des communications en temps réel
> Un schéma du fonctionnement global de l'application est disponible dans le dossier final de rendus.
### 8. Concepts de programmation
Nous avons appliqué plusieurs concepts de programmation :
- Programmation orientée objet (POO) côté backend (Laravel)
- Composants réutilisables côté frontend (React)
- Gestion d’état via les Contexts React
- API REST pour la communication entre frontend et backend
- Programmation modulaire pour une meilleure organisation du code
Pour améliorer la qualité et la robustesse du code, nous avons migré une partie du projet de JavaScript vers TypeScript.
Cette conversion permet :
- Moins d’erreurs grâce au typage statique.
- Un code plus lisible et documenté avec des interfaces claires.
- Une meilleure collaboration grâce à des types explicites.
Les interfaces TypeScript sont regroupées dans des fichiers dédiés, ce qui centralise les définitions et simplifie les mises à jour.
### 9. Tests unitaires
Nous avons mis en place quelques tests unitaires sur certaines fonctions utilitaires, par exemple :
- `utils/auth/login.test.js`
- `utils/auth/logout.test.js`
Suite à un échange avec notre professeur, il nous a été indiqué qu’un nombre limité de tests suffisait pour démontrer notre compréhension et notre mise en pratique de ce concept. En effet, tester l’ensemble du projet aurait demandé un temps de développement trop important au regard des contraintes du projet.
Nous avons donc choisi de nous concentrer sur quelques cas représentatifs, afin de valider notre approche des tests unitaires tout en respectant les délais impartis.
### 10. Intégration continu
Dans notre projet, l’intégration continue repose sur les outils et pratiques utilisés tout au long du développement.
Nous avons mis en place tous les outils qui permettent à termes l'intégration continu de l'application :
- Utilisation de Git pour gérer les versions du projet et intégrer régulièrement les modifications de chaque membre de l’équipe
- Utilisation de Docker pour garantir un environnement de développement et de déploiement identique et reproductible
- Tests réguliers des fonctionnalités à chaque ajout de code (tests manuels et tests API avec Bruno)
- Validation continue du projet, afin de s’assurer qu’une version fonctionnelle reste disponible à tout moment
+18
View File
@@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[compose.yaml]
indent_size = 4
+11
View File
@@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore
+28
View File
@@ -0,0 +1,28 @@
*.log
.DS_Store
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
/.fleet
/.idea
/.nova
/.phpunit.cache
/.vscode
/.zed
/auth.json
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/storage/pail
/vendor
Homestead.json
Homestead.yaml
Thumbs.db
/public/**
!/public/index.php
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
$credentials = $request->validate([
'email' => ['required','email'],
'password' => ['required'],
]);
if (!Auth::attempt($credentials, remember: true)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$request->session()->regenerate();
return response()->json(['message' => 'ok']);
}
public function logout(Request $request): JsonResponse
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json(['message' => 'ok']);
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
abstract class Controller
{
use AuthorizesRequests;
}
+208
View File
@@ -0,0 +1,208 @@
<?php
namespace App\Http\Controllers;
use App\Mail\EventParticipationCancelledMail;
use App\Models\Event;
use App\Models\Task;
use App\Notifications\EventDeleted;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
class EventsController extends Controller
{
public function create(Request $request): JsonResponse
{
if (Gate::denies('create', Event::class)) {
return response()->json(['message' => 'Forbidden'], 403);
}
try {
Event::create([
"name" => $request['name'],
"description" => $request['description'],
"start" => $request['start'],
"end" => $request['end'],
]);
return response()->json(['message' => 'Event created']);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function update(Request $request, int $id): JsonResponse {
if (Gate::denies('update', Event::class)) {
return response()->json(['message' => 'Forbidden'], 403);
}
try {
$event = Event::find($id);
$event->name = $request['name'];
$event->description = $request['description'];
$event->start = $request['start'];
$event->end = $request['end'];
$event->save();
return response()->json(["message" => "Event updated"]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getEvents(Request $request): JsonResponse {
try {
$events = Event::with('tasks')->get();
return response()->json(['data' => $events]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function delete(Request $request, int $id): JsonResponse
{
if (Gate::denies('delete', Event::class)) {
return response()->json(['message' => 'Forbidden'], 403);
}
try {
$event = Event::with('tasks.users')->findOrFail($id);
if (Carbon::now()->lessThanOrEqualTo($event->end)) {
$participants = $event->tasks
->flatMap(fn($task) => $task->users)
->unique('id');
if ($participants->isNotEmpty()) {
Notification::send($participants, new EventDeleted($event));
foreach ($participants as $user) {
if ($user->email_notifications) {
Mail::to($user->email)->send(new EventParticipationCancelledMail([
'name' => $user->name,
'lastname' => $user->lastname,
'eventName' => $event->name,
'start' => $event->start,
'end' => $event->end,
]));
}
}
}
}
$event->delete();
return response()->json(['message' => 'Event deleted and participants notified']);
} catch (\Exception $e) {
Log::error("Event delete error: " . $e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function getEvent(Request $request, int $id): JsonResponse {
try {
$event = Event::with(['tasks' => function ($query) {
$query->withCount('users');
}])->find($id);
return response()->json([
"id" => $event->id,
"name" => $event->name,
"description" => $event->description,
"start" => $event->start,
"end" => $event->end,
"created_at" => $event->created_at,
"updated_at" => $event->updated_at,
"tasks" => $event->tasks,
"event_photo_path" =>$event->event_photo_path,
]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function isUserAssigned(Request $request, int $id): JsonResponse
{
try {
$task = Task::findOrFail($id);
$isAssigned = $task->users()
->where('users.id', $request->user()->id)
->exists();
return response()->json([
'assigned' => $isAssigned
], 200);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json([
'message' => 'Server error'
], 500);
}
}
public function show(int $id)
{
$event = Event::findOrFail($id);
return response()->json([
'event_photo_path' => $event->event_photo_path
]);
}
public function updatePhoto(Request $request, int $id)
{
$request->validate([
'photo' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$event = Event::findOrFail($id);
$path = $request->file('photo')->store('event-photos', 'public');
if ($event->event_photo_path) {
Storage::disk('public')->delete($event->event_photo_path);
}
$event->update([
'event_photo_path' => $path
]);
return response()->json([
'event_photo_path' => $path
]);
}
public function destroy(int $id)
{
$event = Event::findOrFail($id);
if ($event->event_photo_path) {
Storage::disk('public')->delete($event->event_photo_path);
$event->update([
'event_photo_path' => null
]);
}
return response()->json([
'message' => 'Photo supprimée'
]);
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Carbon\Carbon;
class ExportController extends Controller
{
public function exportIcs($userId)
{
$user = User::with('tasks.event')->findOrFail($userId);
$lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//VotreApp//FR',
];
$now = Carbon::now()->utc()->format('Ymd\THis\Z');
foreach ($user->tasks as $task) {
// On ignore les tâches sans dates
if (!$task->start || !$task->end) {
continue;
}
$uid = $task->id . '-' . $user->id . '@votreapp.local';
$dtstart = Carbon::parse($task->start)->utc()->format('Ymd\THis\Z');
$dtend = Carbon::parse($task->end)->utc()->format('Ymd\THis\Z');
$summary = $this->escapeText($task->name);
$description = $this->escapeText(
trim(
($task->description ?? '') .
($task->event ? "\nÉvénement: {$task->event->name}" : '')
)
);
$location = $this->escapeText($task->location ?? '');
$lines[] = 'BEGIN:VEVENT';
$lines[] = 'UID:' . $uid;
$lines[] = 'DTSTAMP:' . $now;
$lines[] = 'DTSTART:' . $dtstart;
$lines[] = 'DTEND:' . $dtend;
$lines[] = 'SUMMARY:' . $summary;
$lines[] = 'DESCRIPTION:' . $description;
$lines[] = 'LOCATION:' . $location;
$lines[] = 'END:VEVENT';
}
$lines[] = 'END:VCALENDAR';
$content = implode("\r\n", $lines) . "\r\n";
return response($content, 200, [
'Content-Type' => 'text/calendar; charset=utf-8',
'Content-Disposition' => 'attachment; filename="agenda_user_' . $user->id . '.ics"',
]);
}
protected function escapeText($text)
{
$text = (string) $text;
return str_replace(
["\\", "\r\n", "\n", "\r", ";", ","],
["\\\\", "\\n", "\\n", "\\n", "\\;", "\\,"],
$text
);
}
}
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class NotificationsController extends Controller
{
public function getNotifications(Request $request): JsonResponse
{
try {
$notifications = $request->user()
->notifications()
->latest()
->get();
return response()->json(['data' => $notifications]);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function deleteNotification(Request $request, string $notificationId): JsonResponse
{
try {
$notification = $request->user()->notifications()->findOrFail($notificationId);
$notification->delete();
return response()->json(['message' => 'Notification supprimée avec succès']);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Erreur lors de la suppression'], 500);
}
}
public function readNotifications(Request $request): JsonResponse
{
try {
$request->user()->unreadNotifications->markAsRead();
return response()->json(['message' => 'Notifications marquées comme lues']);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers;
use Exception;
use Illuminate\Http\Request;
use Typesense\Client;
class SearchController extends Controller
{
protected Client $client;
public function __construct()
{
$this->client = new Client([
'api_key' => config('app.typesense_api_key'),
'nodes' => [
[
'host' => config('app.typesense_connection'),
'port' => '8108',
'protocol' => 'http',
],
],
]);
}
public function searchEvents(Request $request)
{
try {
$query = $request->input('query', '');
return response()->json(
$this->client->collections['events']
->documents
->search([
'q' => $query,
'query_by' => 'name,description',
])
);
} catch (Exception $e) {
return response()->json([
'error' => $e->getMessage()
], 500);
}
}
public function searchUsers(Request $request)
{
try {
$query = $request->input('query', '');
return response()->json(
$this->client->collections['users']
->documents
->search([
'q' => $query,
'query_by' => 'name,lastname',
'filter_by' => 'validate:=true',
])
);
} catch (Exception $e) {
return response()->json([
'error' => $e->getMessage()
], 500);
}
}
}
+286
View File
@@ -0,0 +1,286 @@
<?php
namespace App\Http\Controllers;
use App\Mail\TaskDateUpdateMail;
use App\Mail\TaskParticipationCancelledMail;
use App\Mail\VolunteerAssignToTaskMail;
use App\Mail\VolunteerUnassignToTaskMail;
use App\Models\Event;
use App\Models\Task;
use App\Models\User;
use App\Notifications\TaskDateUpdated;
use App\Notifications\TaskDeleted;
use App\Notifications\VolunteerAssignedToTask;
use App\Notifications\VolunteerUnassignedFromTask;
use App\Services\FormatDate;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
class TasksController extends Controller
{
public function create(Request $request): JsonResponse {
if(Gate::denies('create', Task::class)) {
return response()->json(["message" => "Can't create task"], 403);
}
try {
$event = Event::find($request["event_id"]);
$event->tasks()->create([
"name" => $request["name"],
"description" => $request["description"],
"start" => $request["start"],
"end" => $request["end"],
"location" => $request["location"],
"max_participants" => $request["max_participants"],
]);
return response()->json(['message' => "Task created"], 200);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function assignSelf(Request $request): JsonResponse
{
try {
$task = Task::findOrFail($request["task_id"]);
if ($task->users()->count() >= $task->max_participants) {
return response()->json(["message" => "Max participants reached"], 403);
}
if ($task->users()->where('users.id', $request->user()->id)->exists()) {
return response()->json(["message" => "Already registered"], 409);
}
$task->users()->attach($request->user()->id);
return response()->json(['message' => "Task assigned"], 200);
} catch (\Throwable $e) {
Log::error($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function assignUser(Request $request, int $id): JsonResponse
{
if (Gate::denies('assignOther', Task::class)) {
return response()->json(['message' => "Can't create task"], 403);
}
try {
$task = Task::findOrFail($request['task_id']);
$event = Event::findOrFail($task->events_id);
$user = User::findOrFail($id);
if ($task->users()->where('user_id', $id)->exists()) {
return response()->json(['message' => 'User already assigned to this task'], 400);
}
if ($task->max_participants === $task->users()->count()) {
return response()->json(['message' => 'Max participants reached'], 403);
}
$task->users()->attach($id);
if (Carbon::now()->lessThanOrEqualTo($task->end)) {
$event = Event::findOrFail($task->events_id);
$user->notify(new VolunteerAssignedToTask($task, $event));
if ($user->email_notifications) {
Mail::to($user->email)->send(new VolunteerAssignToTaskMail([
'name' => $user->name,
'lastname' => $user->lastname,
'taskName' => $task->name,
'start' => $task->start,
'end' => $task->end,
'eventName' => $event->name,
]));
} }
return response()->json(['message' => 'Task assigned'], 200);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function getEventTasks(Request $request, int $id): JsonResponse {
try {
$event = Event::find($id);
return response()->json(['data' => $event->tasks()->get()]);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function deleteTask(Request $request, int $id): JsonResponse
{
if (Gate::denies('delete', Task::class)) {
return response()->json(['message' => "Can't delete task"], 403);
}
try {
$task = Task::with('users')->findOrFail($id);
$event = Event::findOrFail($task->events_id);
if (Carbon::now()->lessThanOrEqualTo($task->end) && $task->users->isNotEmpty()) {
Notification::send($task->users, new TaskDeleted($task, $event));
foreach ($task->users as $user) {
if ($user->email_notifications) {
Mail::to($user->email)->send(new TaskParticipationCancelledMail([
'name' => $user->name,
'lastname' => $user->lastname,
'taskName' => $task->name,
'start' => $event->start,
'end' => $task->end,
'eventName' => $event->name,
]));
}
}
}
$task->delete();
return response()->json(['message' => 'Task deleted and participants notified'], 200);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function update(Request $request, int $id): JsonResponse
{
if (Gate::denies('update', Task::class)) {
return response()->json(['message' => 'Forbidden'], 403);
}
try {
$task = Task::with('users')->findOrFail($id);
$datesChanged = $task->start !== $request['start'] || $task->end !== $request['end'];
if ($datesChanged && Carbon::now()->lessThanOrEqualTo($task->end)) {
$event = Event::findOrFail($task->events_id);
Notification::send($task->users, new TaskDateUpdated(
task: $task,
event: $event,
oldStart: FormatDate::formatDate($task->start),
oldEnd: FormatDate::formatDate($task->end),
newStart: FormatDate::formatDate($request['start']),
newEnd: FormatDate::formatDate($request['end']),
));
foreach ($task->users as $user) {
if ($user->email_notifications) {
Mail::to($user->email)->send(new TaskDateUpdateMail([
'name' => $user->name,
'lastname' => $user->lastname,
'taskName' => $task->name,
'oldStart' => FormatDate::formatDate($task->start),
'oldEnd' => FormatDate::formatDate($task->end),
'newStart' => $request['start'],
'newEnd' => $request['end'],
'eventName' => $event->name,
]));
}
}
}
$task->update([
'name' => $request['name'],
'description' => $request['description'],
'start' => $request['start'],
'end' => $request['end'],
'location' => $request['location'],
'max_participants' => $request['max_participants'],
]);
return response()->json(['message' => 'Task updated'], 200);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function unassignSelf(Request $request): JsonResponse {
try {
$task = Task::find($request["task_id"]);
$userId = $request->user()->id;
if (!$task) {
return response()->json(["message" => "Task not found"], 404);
}
$task->users()->detach($userId);
return response()->json(['message' => "Successfully unassigned from task"], 200);
} catch (\Exception $e) {
Log::error("UnassignSelf error: " . $e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function unassignUser(Request $request, int $id): JsonResponse
{
if (Gate::denies('unassignOther', Task::class)) {
return response()->json(['message' => 'Unauthorized'], 403);
}
try {
$task = Task::findOrFail($request['task_id']);
$user = User::findOrFail($id);
if (!$task->users()->where('user_id', $id)->exists()) {
return response()->json(['message' => 'User is not assigned to this task'], 400);
}
$task->users()->detach($id);
if (Carbon::now()->lessThanOrEqualTo($task->end)) {
$event = Event::findOrFail($task->events_id);
$user->notify(new VolunteerUnassignedFromTask($task, $event));
if ($user->email_notifications) {
Mail::to($user->email)->send(new VolunteerUnassignToTaskMail([
'name' => $user->name,
'lastname' => $user->lastname,
'taskName' => $task->name,
'start' => $task->start,
'end' => $task->end,
'eventName' => $event->name,
]));
}
}
return response()->json(['message' => 'User unassigned successfully'], 200);
} catch (\Exception $e) {
Log::error("UnassignUser error: " . $e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
}
+495
View File
@@ -0,0 +1,495 @@
<?php
namespace App\Http\Controllers;
use App\Mail\CreateUserByAdminMail;
use App\Mail\DeactivateAccountMail;
use App\Mail\DeleteAccountMail;
use App\Mail\EmailChangeConfirmedMail;
use App\Mail\NotifyEmailChangeMail;
use App\Mail\RegisterMail;
use App\Mail\RequestEmailChangeMail;
use App\Mail\UpdatePasswordMail;
use App\Mail\UpdateRoleMail;
use App\Mail\ValidateMail;
use App\Models\User;
use App\Notifications\UserRegistered;
use App\Notifications\VolunteerRoleUpdated;
use App\Services\GetRole;
use App\Services\UserService;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
use Pest\Support\Str;
use Propaganistas\LaravelPhone\PhoneNumber;
class UserController extends Controller
{
public function me(Request $request): JsonResponse {
try {
return response()->json(UserService::getData($request->user()->id));
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function create(Request $request): JsonResponse {
$request->validate([
'phone' => ['required', 'phone:FR,national'],
'email' => ['required', 'email:strict,rfc', 'unique:users,email'],
]);
$phone = new PhoneNumber($request["phone"], 'FR');
$user = User::create([
"name" => $request["name"],
"email" => $request["email"],
"password" => Hash::make($request["password"]),
"lastname" => $request["lastname"],
"validate" => 0,
"phone" => $phone->formatInternational(),
"web_notifications" => 1,
"email_notifications" => 1,
]);
$admins = User::whereIn('role', [1, 2])->get();
Notification::sendNow($admins, new UserRegistered($user));
foreach ($admins as $admin) {
if ($admin->email_notifications) {
Mail::to($admin->email)->send(new RegisterMail([
'adminName' => $admin->name,
'adminLastname' => $admin->lastname,
'userName' => $user->name,
'userLastname' => $user->lastname,
]));
}
}
return response()->json(['message' => 'User created successfully']);
}
public function createUserByAdmin(Request $request): JsonResponse {
if(Gate::denies('createOther', $request->user())){
return response()->json(["message" => "Can't create this user"], 403);
}
$request->validate([
'phone' => ['required', 'phone:FR,national'],
'email' => ['required', 'email:strict,rfc', 'unique:users,email'],
]);
$phone = new PhoneNumber($request["phone"], 'FR');
$password = Str::random(12);
$user = User::create([
"name" => $request["name"],
"email" => $request["email"],
"password" => Hash::make($password),
"lastname" => $request["lastname"],
"validate" => 1,
"role" => 3,
"phone" => $phone->formatInternational(),
"web_notifications" => 1,
"email_notifications" => 1,
]);
if ($user->email_notifications) {
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
];
Mail::to($user->email)->send(new CreateUserByAdminMail($data));
}
return response()->json([
'message' => 'User created successfully',
"password" => $password,
]);
}
public function delete(Request $request): JsonResponse {
try {
$user = User::find($request->user()->id);
if ($user->email_notifications) {
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
];
Mail::to($user->email)->send(new DeleteAccountMail($data));
}
$user->delete();
return response()->json(['message' => 'User deleted successfully']);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getUser(Request $request, int $id): JsonResponse {
try {
return response()->json(UserService::getData($id));
} catch (ModelNotFoundException $e) {
return response()->json(['message' => 'Utilisateur non trouvé'], 404);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function deleteById(Request $request, int $id): JsonResponse {
if(Gate::denies('deleteOther', $request->user())){
return response()->json(["message" => "Can't delete this user"], 403);
}
try {
$user = User::find($id);
if ($user->email_notifications) {
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
];
Mail::to($user->email)->send(new DeleteAccountMail($data));
}
$user->delete();
return response()->json(['message' => 'User deleted successfully']);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function deactivate(Request $request, int $id): JsonResponse {
$userToDeactivate = User::findOrFail($id);
if(Gate::denies('deactivate', $userToDeactivate)){
return response()->json(["message" => "Can't delete this user"], 403);
}
try {
$userToDeactivate->update(['validate' => 0]);
if ($userToDeactivate->email_notifications) {
$data = [
'name' => $userToDeactivate->name,
'lastname' => $userToDeactivate->lastname,
];
Mail::to($userToDeactivate->email)->send(new DeactivateAccountMail($data));
}
return response()->json(['message' => 'User deactivated successfully']);
} catch (ModelNotFoundException $e) {
return response()->json(['message' => 'Utilisateur non trouvé'], 404);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function update(Request $request): JsonResponse {
try {
$phone = new PhoneNumber($request["phone"], 'FR');
$user = User::find($request->user()->id);
$user->name = $request["name"];
$user->lastname = $request["lastname"];
$user->phone = $phone;
$user->save();
return response()->json(['message' => 'User updated successfully']);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function validate(Request $request, int $id): JsonResponse {
if(Gate::denies('validate', $request->user())){
return response()->json(["message" => "Can't validate this user"], 403);
}
try {
$user = User::find($id);
$user->validate = 1;
$user->role = 3;
$user->verified_at = now();
$user->save();
if ($user->email_notifications) {
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
];
Mail::to($user->email)->send(new ValidateMail($data));
}
return response()->json(['message' => 'User validated successfully']);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function addRole(Request $request, int $id): JsonResponse {
if(Gate::denies('addRole', $request->user())){
return response()->json(["message" => "Can't add role to this user"], 403);
}
try {
$user = User::find($id);
$user->role = $request['role'];
$user->save();
$role = GetRole::getRole($request['role']);
$user->notify(new VolunteerRoleUpdated($role));
if ($user->email_notifications) {
Mail::to($user->email)->send(new UpdateRoleMail([
'name' => $user->name,
'lastname' => $user->lastname,
'role' => $role,
]));
}
return response()->json(['message' => 'User role added successfully']);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getUsers(Request $request): JsonResponse {
try {
$users = User::where('validate', 1)
->orderBy('verified_at', 'desc')
->pluck('id');
return response()->json($users);
} catch(\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getUserTasks(Request $request): JsonResponse {
try {
return response()->json(['data' => $request->user()->tasks]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getUserByIdTasks(Request $request, int $id): JsonResponse {
try {
return response()->json(['data' => User::find($id)->tasks]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getUserTaskById(Request $request, int $idTask): JsonResponse {
try {
return response()->json(['data' => $request->user()->tasks->find($idTask)]);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function getInvalidUsers(Request $request): JsonResponse {
if(Gate::denies('getInvalidUsers', $request->user())){
return response()->json(["message" => "Can't add role to this user"], 403);
}
try {
$users = User::where('validate', 0)->pluck('id');
return response()->json($users);
} catch (\Exception $e) {
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function toggleEmailNotifications(Request $request, int $id): JsonResponse {
try {
$user = User::findOrFail($id);
$user->email_notifications = $user->email_notifications ? 0 : 1;
$user->save();
return response()->json($user);
} catch(\Exception $e){
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function toggleWebNotifications(Request $request, int $id): JsonResponse {
try {
$user = User::findOrFail($id);
$user->web_notifications = $user->web_notifications ? 0 : 1;
$user->save();
return response()->json($user);
} catch(\Exception $e){
Log::info($e->getMessage());
return response()->json(['message' => "Server error"], 500);
}
}
public function updatePassword(Request $request): JsonResponse {
try {
$request->validate([
'current_password' => ['required'],
'new_password' => ['required', 'confirmed'],
]);
$user = $request->user();
if (!Hash::check($request['current_password'], $user->password)) {
return response()->json(['message' => 'Mot de passe actuel incorrect'], 403);
}
$user->password = Hash::make($request['new_password']);
$user->save();
if ($user->email_notifications) {
Mail::to($user->email)->send(new UpdatePasswordMail([
'name' => $user->name,
'lastname' => $user->lastname,
]));
}
return response()->json(['message' => 'Mot de passe mis à jour avec succès']);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function requestEmailChange(Request $request): JsonResponse
{
try {
$request->validate([
'email' => ['required', 'email:strict,rfc', 'unique:users,email'],
]);
$user = $request->user();
$token = \Str::random(64);
$user->pending_email = $request['email'];
$user->email_change_token = hash('sha256', $token);
$user->email_change_token_expires_at = now()->addHour();
$user->save();
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
'token' => $token
];
Mail::to($user->email)->send(new NotifyEmailChangeMail($data));
Mail::to($request['email'])->send(new RequestEmailChangeMail($data));
return response()->json(['message' => 'Un email de confirmation a été envoyé à votre nouvelle adresse.']);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function confirmEmailChange(Request $request): JsonResponse
{
try {
$request->validate([
'token' => ['required', 'string'],
]);
$user = User::where('email_change_token', hash('sha256', $request['token']))
->where('email_change_token_expires_at', '>', now())
->firstOrFail();
$newEmail = $user->pending_email;
$oldEmail = $user->email;
$user->email = $newEmail;
$user->pending_email = null;
$user->email_change_token = null;
$user->email_change_token_expires_at = null;
$user->save();
$data = [
'name' => $user->name,
'lastname' => $user->lastname,
'newEmail' => $newEmail
];
Mail::to($newEmail)->send(new EmailChangeConfirmedMail($data));
Mail::to($oldEmail)->send(new EmailChangeConfirmedMail($data));
return response()->json(['message' => 'Email mis à jour avec succès']);
} catch (ModelNotFoundException $e) {
return response()->json(['message' => 'Token invalide ou expiré'], 400);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Server error'], 500);
}
}
public function show(Request $request)
{
$user = $request->user();
return response()->json(['profile_photo_path' => $user->profile_photo_path]);
}
public function updatePhoto(Request $request)
{
$request->validate([
'photo' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
$user = $request->user();
$path = $request->file('photo')->store('profile-photos', 'public');
// Supprimer l'ancienne photo si elle existe
if ($user->profile_photo_path) {
Storage::disk('public')->delete($user->profile_photo_path);
}
$user->update(['profile_photo_path' => $path]);
return response()->json(['profile_photo_path' => $path]);
}
public function destroy(Request $request)
{
$user = $request->user();
if ($user->profile_photo_path) {
Storage::disk('public')->delete($user->profile_photo_path);
$user->update(['profile_photo_path' => null]);
}
return response()->json(['message' => 'Photo supprimée']);
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CreateUserByAdminMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Compte créé - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.createUserByAdmin',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class DeactivateAccountMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Compte désactivé - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.deactivateAccount',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class DeleteAccountMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Compte supprimé - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.deleteAccount',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class EmailChangeConfirmedMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: "Validation de changement d'adresse mail - Comité des fêtes de Beaupont",
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.emailChangeConfirmed',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'newEmail' => $this->data['newEmail'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace App\Mail;
use App\Services\FormatDate;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class EventParticipationCancelledMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Evénement supprimé - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.eventParticipationCancelled',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'eventName' => $this->data['eventName'],
'start' => FormatDate::formatDate($this->data['start']),
'end' => FormatDate::formatDate($this->data['end']),
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class NotifyEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: "Demande de changement d'adresse mail - Comité des fêtes de Beaupont",
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.notifyEmailChange',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class RegisterMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Nouvelle demande de validation - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.register',
with: [
'adminName' => $this->data['adminName'],
'adminLastname' => $this->data['adminLastname'],
'userName' => $this->data['userName'],
'userLastname' => $this->data['userLastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class RequestEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: "Demande de changement d'adresse mail - Comité des fêtes de Beaupont",
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.requestEmailChange',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'token' => $this->data['token'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class TaskDateUpdateMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Date de tâche modifiée - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.updateRole',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'taskName' => $this->data['taskName'],
'oldStart' => $this->data['oldStart'],
'oldEnd' => $this->data['oldEnd'],
'newStart' => $this->data['newStart'],
'newEnd' => $this->data['newEnd'],
'eventName' => $this->data['eventName'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\Mail;
use App\Services\FormatDate;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class TaskParticipationCancelledMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Tâche supprimée - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.taskParticipationCancelled',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'taskName' => $this->data['taskName'],
'end' => FormatDate::formatDate($this->data['end']),
'start' => FormatDate::formatDate($this->data['start']),
'eventName' => $this->data['eventName'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class UpdatePasswordMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Mot de passe mis à jour - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.updatePassword',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class UpdateRoleMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Rôle mis à jour - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.updateRole',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'role' => $this->data['role'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class ValidateMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Compte validé - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.validate',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\Mail;
use App\Services\FormatDate;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class VolunteerAssignToTaskMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Assignation - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.volunteerAssignToTask',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'taskName' => $this->data['taskName'],
'start' => FormatDate::formatDate($this->data['start']),
'end' => FormatDate::formatDate($this->data['end']),
'eventName' => $this->data['eventName'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace App\Mail;
use App\Services\FormatDate;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class VolunteerUnassignToTaskMail extends Mailable
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Désassignation - Comité des fêtes de Beaupont',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mails.volunteerUnassignToTask',
with: [
'name' => $this->data['name'],
'lastname' => $this->data['lastname'],
'taskName' => $this->data['taskName'],
'start' => FormatDate::formatDate($this->data['start']),
'end' => FormatDate::formatDate($this->data['end']),
'eventName' => $this->data['eventName'],
]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
use HasFactory;
protected $fillable = [
"name",
"description",
"event_photo_path",
'start',
'end',
];
public function tasks() {
return $this->hasMany(Task::class, 'events_id');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
protected $fillable = [
'name',
'start',
'end',
'location',
'description',
'max_participants',
];
public function event() {
return $this->belongsTo(Event::class);
}
public function users() {
return $this->belongsToMany(User::class);
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'lastname',
'email',
'password',
'phone',
'validate',
'role',
'verified_at',
'profile_photo_path',
'email_notifications',
'web_notifications'
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function tasks() {
return $this->belongsToMany(Task::class);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Notifications;
use App\Models\Event;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class EventDeleted extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(public Event $event) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "L'événement {$this->event->name} a été supprimé. Vous n'y participez donc plus.",
'event_id' => $this->event->id,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace App\Notifications;
use App\Models\Event;
use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class TaskDateUpdated extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(
public Task $task,
public Event $event,
public string $oldStart,
public string $oldEnd,
public string $newStart,
public string $newEnd,
) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "La date de la tâche {$this->task->name} de l'événement {$this->event->name} a été mise à jour. Cette tâche aura maintenant lieu du {$this->newStart} au {$this->newEnd}.",
'task_id' => $this->task->id,
'event_id' => $this->event->id,
'old_start' => $this->oldStart,
'old_end' => $this->oldEnd,
'new_start' => $this->newStart,
'new_end' => $this->newEnd,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Notifications;
use App\Models\Event;
use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class TaskDeleted extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(
public Task $task,
public Event $event,
) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "La tâche {$this->task->name} pour l'événement {$this->event->name} a été supprimée. Vous n'y participez donc plus.",
'task_id' => $this->task->id,
'event_id' => $this->event->id,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Notifications;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class UserRegistered extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(public User $user) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "Nouvelle demande d'inscription : {$this->user->name} {$this->user->lastname}",
'user_id' => $this->user->id,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Notifications;
use App\Models\Event;
use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class VolunteerAssignedToTask extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(
public Task $task,
public Event $event,
) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "Vous avez été assigné à la tâche {$this->task->name} de l'événement {$this->event->name}.",
'task_id' => $this->task->id,
'event_id' => $this->event->id,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class VolunteerRoleUpdated extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(public string $role) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "Votre rôle a changé pour : {$this->role}",
'role' => $this->role,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Notifications;
use App\Models\Event;
use App\Models\Task;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
class VolunteerUnassignedFromTask extends Notification implements ShouldBroadcast
{
use Queueable;
public function __construct(
public Task $task,
public Event $event,
) {}
public function via($notifiable): array
{
$channels = [];
if ($notifiable->web_notifications) {
$channels[] = 'database';
$channels[] = 'broadcast';
}
return $channels;
}
public function toDatabase($notifiable): array
{
return [
'message' => "Vous avez été désassigné de la tâche {$this->task->name} de l'événement {$this->event->name}.",
'task_id' => $this->task->id,
'event_id' => $this->event->id,
];
}
public function toBroadcast($notifiable): BroadcastMessage
{
return new BroadcastMessage($this->toDatabase($notifiable));
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Observers;
use App\Models\Event;
use App\Services\TypesenseService;
class EventObserver
{
public function created(Event $event)
{
app(TypesenseService::class)->upsertEvent($event);
}
public function updated(Event $event)
{
app(TypesenseService::class)->upsertEvent($event);
}
public function deleted(Event $event)
{
app(TypesenseService::class)->deleteEvent($event->id);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Observers;
use App\Models\User;
use App\Services\TypesenseService;
class UserObserver
{
public function created(User $user)
{
app(TypesenseService::class)->upsertUser($user);
}
public function updated(User $user)
{
app(TypesenseService::class)->upsertUser($user);
}
public function deleted(User $user)
{
app(TypesenseService::class)->deleteUser($user->id);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Policies;
use App\Models\User;
class EventsPolicy
{
public function create(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function update(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function delete(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Policies;
use App\Models\User;
class TasksPolicy
{
public function create(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function assignOther(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function unassignOther(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function delete(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function update(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Policies;
use App\Models\User;
class UsersPolicy
{
public function deleteOther(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function createOther(User $user): bool {
return $user->role === 1 || $user->role === 2;
}
public function deactivate(User $user, User $userToDeactivate): bool
{
return in_array($user->role, [1, 2])
&& $user->role < $userToDeactivate->role
&& $user->id !== $userToDeactivate->id;
}
public function validate(User $user): bool
{
return $user->role === 1 || $user->role === 2;
}
public function addRole(User $user): bool
{
return $user->role === 1;
}
public function getInvalidUsers(User $user) : bool
{
return $user->role === 1 || $user->role === 2;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Providers;
use App\Models\Event;
use App\Models\User;
use App\Observers\EventObserver;
use App\Observers\UserObserver;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void {
Route::middleware('web')->prefix('api')
->group($this->app->basePath('routes/users.php'));
Route::middleware('web')->prefix('api')
->group($this->app->basePath('routes/auth.php'));
Route::middleware('web')->prefix('api')
->group($this->app->basePath('routes/events.php'));
Route::middleware('web')->prefix('api')
->group($this->app->basePath('routes/notifications.php'));
Route::middleware('web')->prefix('api')
->group($this->app->basePath('routes/channels.php'));
User::observe(UserObserver::class);
Event::observe(EventObserver::class);
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
public function boot(): void
{
Broadcast::routes(['middleware' => ['web', 'auth:sanctum']]);
require base_path('routes/channels.php');
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Providers;
use App\Models\Event;
use App\Models\Task;
use App\Models\User;
use App\Policies\EventsPolicy;
use App\Policies\TasksPolicy;
use App\Policies\UsersPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class GateServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot(): void
{
Gate::policy(User::class, UsersPolicy::class);
Gate::policy(Event::class, EventsPolicy::class);
Gate::policy(Task::class, TasksPolicy::class);
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Services;
use Carbon\Carbon;
class FormatDate
{
public static function formatDate($date){
return Carbon::parse($date)->format('d/m/Y, H:i');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Services;
class GetRole
{
public static function getRole($role)
{
$role = (int)$role;
return match ($role) {
1 => "Président",
2 => "Gérant",
3 => "Bénévole",
default => "Utilisateur",
};
}
public static function GetAllRoles() {
return [
"Président",
"Gérant",
"Bénévole"
];
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
namespace App\Services;
use App\Models\Event;
use App\Models\User;
use Typesense\Client;
class TypesenseService
{
protected Client $client;
public function __construct()
{
$this->client = new Client([
'api_key' => config('app.typesense_api_key'),
'nodes' => [
[
'host' => config('app.typesense_connection'),
'port' => '8108',
'protocol' => 'http',
],
],
]);
}
private function ensureCollectionsExist(): void
{
static $checked = false;
if ($checked) {
return;
}
try {
$collections = $this->client->collections->retrieve();
$names = array_column($collections, 'name');
if (!in_array('events', $names)) {
$this->client->collections->create([
'name' => 'events',
'fields' => [
['name' => 'name', 'type' => 'string'],
['name' => 'description', 'type' => 'string'],
],
]);
}
if (!in_array('users', $names)) {
$this->client->collections->create([
'name' => 'users',
'fields' => [
['name' => 'name', 'type' => 'string'],
['name' => 'lastname', 'type' => 'string'],
['name' => 'validate', 'type' => 'bool'],
],
]);
}
$checked = true;
} catch (\Throwable $e) {
report($e);
}
}
public function upsertEvent(Event $event): void
{
$this->ensureCollectionsExist();
try {
$this->client->collections['events']
->documents
->upsert([
'id' => (string) $event->id,
'name' => $event->name,
'description' => $event->description,
]);
} catch (\Throwable $e) {
report($e);
}
}
public function deleteEvent(int $eventId): void
{
$this->ensureCollectionsExist();
try {
$this->client->collections['events']
->documents[(string) $eventId]
->delete();
} catch (\Throwable $e) {
report($e);
}
}
public function upsertUser(User $user): void
{
$this->ensureCollectionsExist();
if (!$user->validate) {
try {
$this->client->collections['users']
->documents[(string) $user->id]
->retrieve();
$this->deleteUser($user->id);
} catch (\Throwable $e) {
}
return;
}
try {
$this->client->collections['users']
->documents
->upsert([
'id' => (string) $user->id,
'name' => $user->name,
'lastname' => $user->lastname,
'validate' => true,
]);
} catch (\Throwable $e) {
report($e);
}
}
public function deleteUser(int $userId): void
{
$this->ensureCollectionsExist();
try {
$this->client->collections['users']
->documents[(string) $userId]
->delete();
} catch (\Throwable $e) {
report($e);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace App\Services;
use App\Models\User;
class UserService
{
public static function getData(int $id) : array {
$user = User::findOrFail($id);
return [
"id" => $user->id,
"name" => $user->name,
"lastname" => $user->lastname,
"email" => $user->email,
"role" => GetRole::getRole($user->role),
"isAdmin" => ($user->role === 1 || $user->role === 2),
"phone" => $user->phone,
"created_at" => $user->created_at,
"updated_at" => $user->updated_at,
"verfied_at" => $user->verfied_at,
"validate" => $user->validate,
"tasks" => $user->tasks,
"profile_photo_path" => $user->profile_photo_path,
"email_notifications" => $user->email_notifications,
"web_notifications" => $user->web_notifications,
];
}
}
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
use Illuminate\Foundation\Application;
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
/** @var Application $app */
$app = require_once __DIR__ . '/bootstrap/app.php';
$status = $app->handleCommand(new ArgvInput);
exit($status);
+23
View File
@@ -0,0 +1,23 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
channels: __DIR__.'/../routes/channels.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->statefulApi();
$middleware->validateCsrfTokens(except: [
'broadcasting/auth',
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+7
View File
@@ -0,0 +1,7 @@
<?php
return [
app\Providers\AppServiceProvider::class,
app\Providers\BroadcastServiceProvider::class,
app\Providers\GateServiceProvider::class,
];
+94
View File
@@ -0,0 +1,94 @@
{
"$schema": "https://getcomposer.org/schema.json",
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": [
"laravel",
"framework"
],
"license": "MIT",
"require": {
"php": "^8.2",
"laravel/framework": "^12.0",
"laravel/reverb": "^1.0",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.10.1",
"propaganistas/laravel-phone": "^6.0",
"typesense/typesense-php": "*"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.24",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"pestphp/pest": "^4.1",
"pestphp/pest-plugin-laravel": "^4.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"setup": [
"composer install",
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"@php artisan key:generate",
"@php artisan migrate --force",
"npm install",
"npm run build"
],
"dev": [
"Composer\\Config::disableProcessTimeout",
"npx concurrently -c \"#93c5fd,#c4b5fd\" \"php artisan serve --host=localhost --port=8000\" \"php artisan pail --timeout=0\" --names=server,logs --kill-others"
],
"test": [
"@php artisan config:clear --ansi",
"@php artisan test"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
],
"pre-package-uninstall": [
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
Generated Executable
+11174
View File
File diff suppressed because it is too large. Load diff
+139
View File
@@ -0,0 +1,139 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application, which will be used when the
| framework needs to place the application's name in a notification or
| other UI elements where an application name needs to be displayed.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| the application so that it's available within Artisan commands.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. The timezone
| is set to "UTC" by default as it is suitable for most use cases.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by Laravel's translation / localization methods. This option can be
| set to any locale for which you plan to have translation strings.
|
*/
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is utilized by Laravel's encryption services and should be set
| to a random, 32 character string to ensure that all encrypted values
| are secure. You should do this prior to deploying the application.
|
*/
'cipher' => 'AES-256-CBC',
'key' => env('APP_KEY'),
'previous_keys' => [
...array_filter(
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
),
],
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
'store' => env('APP_MAINTENANCE_STORE', 'database'),
],
'typesense_api_key' => env('TYPESENSE_API_KEY'),
'typesense_connection' => env('TYPESENSE_CONNECTION'),
'dev' => env('DEV'),
'admin_name' => env('ADMIN_NAME') ?:'admin',
'admin_lastname' => env('ADMIN_LASTNAME') ?: 'admin',
'admin_email' => env('ADMIN_MAIL') ?: 'admin@mail.com',
'admin_password' => env('ADMIN_PASSWORD') ?: 'admin',
'admin_phone' => env('ADMIN_PHONE_NUMBER') ?: '0102030405',
'vite_api_url' => env('VITE_API_URL') ?: 'http://localhost:80',
];
+115
View File
@@ -0,0 +1,115 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option defines the default authentication "guard" and password
| reset "broker" for your application. You may change these values
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => env('AUTH_GUARD', 'web'),
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| which utilizes session storage plus the Eloquent user provider.
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| If you have multiple user tables or models you may configure multiple
| providers to represent the model / table. These providers may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => env('AUTH_MODEL', App\Models\User::class),
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| These configuration options specify the behavior of Laravel's password
| reset functionality, including the table utilized for token storage
| and the user provider that is invoked to actually retrieve users.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the number of seconds before a password confirmation
| window expires and users are asked to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
];
+82
View File
@@ -0,0 +1,82 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "reverb", "pusher", "ably", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_CONNECTION', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over WebSockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'options' => [
'host' => env('REVERB_HOST'),
'port' => env('REVERB_PORT', 443),
'scheme' => env('REVERB_SCHEME', 'https'),
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
'encrypted' => true,
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];
+108
View File
@@ -0,0 +1,108 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache store that will be used by the
| framework. This connection is utilized if another isn't explicitly
| specified when running a cache operation inside the application.
|
*/
'default' => env('CACHE_STORE', 'database'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "array", "database", "file", "memcached",
| "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'connection' => env('DB_CACHE_CONNECTION'),
'table' => env('DB_CACHE_TABLE', 'cache'),
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
| stores, there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
];
+37
View File
@@ -0,0 +1,37 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => explode(',', env('CORS_PATHS', 'api/*')),
'allowed_methods' => ['*'],
'allowed_origins' => env('DEV', 0) == 1
? explode(',', env('DEV_CORS_ALLOWED_ORIGINS'))
: explode(',', env('PROD_CORS_ALLOWED_ORIGINS')),
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
+183
View File
@@ -0,0 +1,183 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for database operations. This is
| the connection which will be utilized unless another connection
| is explicitly specified when you execute a query / statement.
|
*/
'default' => env('DB_CONNECTION', 'sqlite'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Below are all of the database connections defined for your application.
| An example configuration is provided for each database system which
| is supported by Laravel. You're free to add / remove connections.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
'transaction_mode' => 'DEFERRED',
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'mariadb' => [
'driver' => 'mariadb',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DB_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run on the database.
|
*/
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => true,
],
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as Memcached. You may define your connection settings here.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
'persistent' => env('REDIS_PERSISTENT', false),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
'max_retries' => env('REDIS_MAX_RETRIES', 3),
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
'max_retries' => env('REDIS_MAX_RETRIES', 3),
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
],
],
];
+80
View File
@@ -0,0 +1,80 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application for file storage.
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Below you may configure as many filesystem disks as necessary, and you
| may even configure multiple disks for the same driver. Examples for
| most supported storage drivers are configured here for reference.
|
| Supported drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'serve' => true,
'throw' => false,
'report' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
'report' => false,
],
'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'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
'report' => false,
],
],
/*
|--------------------------------------------------------------------------
| 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'),
],
];
+132
View File
@@ -0,0 +1,132 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that is utilized to write
| messages to your logs. The value provided here should match one of
| the channels present in the list of "channels" configured below.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Laravel
| utilizes the Monolog PHP logging library, which includes a variety
| of powerful log handlers and formatters that you're free to use.
|
| Available drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog", "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
'level' => env('LOG_LEVEL', 'critical'),
'replace_placeholders' => true,
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'handler_with' => [
'stream' => 'php://stderr',
],
'formatter' => env('LOG_STDERR_FORMATTER'),
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
];
+118
View File
@@ -0,0 +1,118 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Mailer
|--------------------------------------------------------------------------
|
| This option controls the default mailer that is used to send all email
| messages unless another mailer is explicitly specified when sending
| the message. All additional mailers can be configured within the
| "mailers" array. Examples of each type of mailer are provided.
|
*/
'default' => env('MAIL_MAILER', 'log'),
/*
|--------------------------------------------------------------------------
| Mailer Configurations
|--------------------------------------------------------------------------
|
| Here you may configure all of the mailers used by your application plus
| their respective settings. Several examples have been configured for
| you and you are free to add your own as your application requires.
|
| Laravel supports a variety of mail "transport" drivers that can be used
| when delivering an email. You may specify which one you're using for
| your mailers below. You may also add additional mailers if needed.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "resend", "log", "array",
| "failover", "roundrobin"
|
*/
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'scheme' => env('MAIL_SCHEME'),
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', '127.0.0.1'),
'port' => env('MAIL_PORT', 2525),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
],
'ses' => [
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],
'resend' => [
'transport' => 'resend',
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
'failover' => [
'transport' => 'failover',
'mailers' => [
'smtp',
'log',
],
'retry_after' => 60,
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
'retry_after' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all emails sent by your application to be sent from
| the same address. Here you may specify a name and address that is
| used globally for all emails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
];
+112
View File
@@ -0,0 +1,112 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue supports a variety of backends via a single, unified
| API, giving you convenient access to each backend using identical
| syntax for each. The default queue connection is defined below.
|
*/
'default' => env('QUEUE_CONNECTION', 'database'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection options for every queue backend
| used by your application. An example configuration is provided for
| each backend supported by Laravel. You're also free to add more.
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'connection' => env('DB_QUEUE_CONNECTION'),
'table' => env('DB_QUEUE_TABLE', 'jobs'),
'queue' => env('DB_QUEUE', 'default'),
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
'after_commit' => false,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
'queue' => env('BEANSTALKD_QUEUE', 'default'),
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
'block_for' => 0,
'after_commit' => false,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => null,
'after_commit' => false,
],
],
/*
|--------------------------------------------------------------------------
| Job Batching
|--------------------------------------------------------------------------
|
| The following options configure the database and table that store job
| batching information. These options can be updated to any database
| connection and table which has been defined by your application.
|
*/
'batching' => [
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'job_batches',
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control how and where failed jobs are stored. Laravel ships with
| support for storing failed jobs in a simple file or in a database.
|
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'failed_jobs',
],
];
+84
View File
@@ -0,0 +1,84 @@
<?php
use Laravel\Sanctum\Sanctum;
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:5173,127.0.0.1,127.0.0.1:8000,::1',
Sanctum::currentApplicationUrlWithPort(),
// Sanctum::currentRequestHost(),
))),
/*
|--------------------------------------------------------------------------
| Sanctum Guards
|--------------------------------------------------------------------------
|
| This array contains the authentication guards that will be checked when
| Sanctum is trying to authenticate a request. If none of these guards
| are able to authenticate the request, Sanctum will use the bearer
| token that's present on an incoming request for authentication.
|
*/
'guard' => ['web'],
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
],
];
+38
View File
@@ -0,0 +1,38 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
'resend' => [
'key' => env('RESEND_KEY'),
],
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
];
+217
View File
@@ -0,0 +1,217 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option determines the default session driver that is utilized for
| incoming requests. Laravel supports a variety of storage options to
| persist session data. Database storage is a great default choice.
|
| Supported: "file", "cookie", "database", "memcached",
| "redis", "dynamodb", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'database'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to expire immediately when the browser is closed then you may
| indicate that via the expire_on_close configuration option.
|
*/
'lifetime' => (int) env('SESSION_LIFETIME', 120),
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it's stored. All encryption is performed
| automatically by Laravel and you may use the session like normal.
|
*/
'encrypt' => env('SESSION_ENCRYPT', false),
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When utilizing the "file" session driver, the session files are placed
| on disk. The default storage location is defined here; however, you
| are free to provide another location where they should be stored.
|
*/
'files' => storage_path('framework/sessions'),
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => env('SESSION_CONNECTION'),
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table to
| be used to store sessions. Of course, a sensible default is defined
| for you; however, you're welcome to change this to another table.
|
*/
'table' => env('SESSION_TABLE', 'sessions'),
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| When using one of the framework's cache driven session backends, you may
| define the cache store which should be used to store the session data
| between requests. This must match one of your defined cache stores.
|
| Affects: "dynamodb", "memcached", "redis"
|
*/
'store' => env('SESSION_STORE'),
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the session cookie that is created by
| the framework. Typically, you should not need to change this value
| since doing so does not grant a meaningful security improvement.
|
*/
'cookie' => env(
'SESSION_COOKIE',
Str::slug((string) env('APP_NAME', 'laravel')).'-session'
),
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application, but you're free to change this when necessary.
|
*/
'path' => env('SESSION_PATH', '/'),
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| This value determines the domain and subdomains the session cookie is
| available to. By default, the cookie will be available to the root
| domain and all subdomains. Typically, this shouldn't be changed.
|
*/
'domain' => env('SESSION_DOMAIN'),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you when it can't be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE'),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. It's unlikely you should disable this option.
|
*/
'http_only' => env('SESSION_HTTP_ONLY', true),
/*
|--------------------------------------------------------------------------
| Same-Site Cookies
|--------------------------------------------------------------------------
|
| This option determines how your cookies behave when cross-site requests
| take place, and can be used to mitigate CSRF attacks. By default, we
| will set this value to "lax" to permit secure cross-site requests.
|
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
| Supported: "lax", "strict", "none", null
|
*/
'same_site' => env('SESSION_SAME_SITE', 'lax'),
/*
|--------------------------------------------------------------------------
| Partitioned Cookies
|--------------------------------------------------------------------------
|
| Setting this value to true will tie the cookie to the top-level site for
| a cross-site context. Partitioned cookies are accepted by the browser
| when flagged "secure" and the Same-Site attribute is set to "none".
|
*/
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
];
+1
View File
@@ -0,0 +1 @@
*.sqlite*
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Database\Factories;
use App\Models\Event;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<Event>
*/
class EventFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => $this->faker->text(15),
'description' => $this->faker->text(50),
];
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'lastname' => fake()->lastname(),
'email' => fake()->unique()->safeEmail(),
'password' => Hash::make('password'),
'remember_token' => Str::random(10),
'validate' => rand(0, 1),
'role' => rand(2, 5)
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
}
};
@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('lastname');
$table->string('email')->unique();
$table->string('password');
$table->string('phone')->nullable();
$table->integer('validate');
$table->integer('role')->nullable();
$table->dateTime('verified_at')->nullable();
$table->integer('email_notifications')->default(1);
$table->integer('web_notifications')->default(1);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('sessions');
}
};
@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
if (!Schema::hasColumn('users', 'remember_token')) {
$table->rememberToken()->nullable();
}
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'remember_token')) {
$table->dropColumn('remember_token');
}
});
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('events', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
$table->string('event_photo_path', 2048)->nullable();
$table->dateTime('start')->nullable();
$table->dateTime('end')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('events');
}
};
@@ -0,0 +1,38 @@
<?php
namespace Database\Migrations;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('description');
$table->string('location');
$table->dateTime("start");
$table->dateTime("end");
$table->integer('max_participants');
$table->foreignId("events_id")->constrained("events")->onDelete("cascade");
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('task_user', function (Blueprint $table) {
$table->id();
$table->foreignId('task_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('profile_photo_path', 2048)->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('profile_photo_path');
});
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('notifications', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('notifications');
}
};
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
if (config('app.dev') == 1) {
$this->call(DevSeeder::class);
} else {
$this->call(ProdSeeder::class);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Database\Seeders;
use App\Models\Event;
use App\Models\User;
use App\Services\TypesenseService;
use Illuminate\Database\Seeder;
class DevSeeder extends Seeder
{
public function run(): void
{
$typesense = app(TypesenseService::class);
$admin = User::firstOrCreate(
['email' => config('app.admin_email')],
[
'name' => config('app.admin_name'),
'lastname' => config('app.admin_lastname'),
'password' => bcrypt(config('app.admin_password')),
'phone' => config('app.admin_phone'),
'role' => 1,
'validate' => 1,
'web_notifications' => 1,
'email_notifications' => 1,
]
);
$typesense->upsertUser($admin);
User::factory()->count(10)->create();
Event::factory()->count(10)->create();
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Database\Seeders;
use App\Models\User;
use App\Services\TypesenseService;
use Illuminate\Database\Seeder;
class ProdSeeder extends Seeder
{
public function run(): void
{
$typesense = app(TypesenseService::class);
$admin = User::firstOrCreate(
['email' => config('app.admin_email')],
[
'name' => config('app.admin_name'),
'lastname' => config('app.admin_lastname'),
'password' => bcrypt(config('app.admin_password')),
'phone' => config('app.admin_phone'),
'role' => 1,
'validate' => 1,
'web_notifications' => 1,
'email_notifications' => 1,
]
);
$typesense->upsertUser($admin);
}
}
+2591
View File
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"scripts": {
"build": "vite build",
"dev": "vite --port 5174"
},
"devDependencies": {
"@tailwindcss/vite": "^4.0.0",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-echo": "^2.2.7",
"laravel-vite-plugin": "^2.0.0",
"pusher-js": "^8.4.0",
"tailwindcss": "^4.0.0",
"vite": "^7.0.7"
}
}
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>tests/Feature</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>app</directory>
</include>
</source>
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="PULSE_ENABLED" value="false"/>
<env name="TELESCOPE_ENABLED" value="false"/>
<env name="NIGHTWATCH_ENABLED" value="false"/>
</php>
</phpunit>
+20
View File
@@ -0,0 +1,20 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Determine if the application is in maintenance mode...
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
// Register the Composer autoloader...
require __DIR__.'/../vendor/autoload.php';
// Bootstrap Laravel and handle the request...
/** @var Application $app */
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->handleRequest(Request::capture());
+14
View File
@@ -0,0 +1,14 @@
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'reverb',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Compte créé</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>
Un compte a été créé pour vous par un administrateur sur le site de gestion des bénévoles
du Comité des fêtes de Beaupont.
</p>
<p>
Vous pouvez dès à présent vous connecter et accéder à votre espace personnel afin de participer
aux activités proposées.
</p>
<p>
Veuillez vous renseigner auprès d'un administrateur pour récupérer vos identifiants.
</p>
<p style="text-align: center; margin: 30px 0;">
<a href="{{ url('/') }}"
style="background-color: #3498db; color: white; padding: 12px 20px; text-decoration: none; border-radius: 5px;">
Se connecter
</a>
</p>
<p>
Si vous n’êtes pas à l’origine de cette création de compte ou si vous avez des questions,
merci de contacter rapidement l’équipe organisatrice.
</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Compte désactivé</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>
Nous vous informons que votre compte sur le site de gestion des bénévoles
du Comité des fêtes de Beaupont a été désactivé par un administrateur.
</p>
<p>
Vous ne pouvez désormais plus accéder à votre espace personnel ni participer aux activités proposées.
</p>
<p>
Si vous pensez qu’il s’agit d’une erreur ou si vous souhaitez obtenir plus d’informations,
nous vous invitons à contacter l’équipe organisatrice.
</p>
<p>
Votre compte pourra être réactivé ultérieurement si nécessaire.
</p>
<p>
À bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Compte supprimé</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>
Nous vous informons que votre compte sur le site de gestion des bénévoles
du Comité des fêtes de Beaupont a été supprimé.
</p>
<p>
Vous n’avez désormais plus accès à votre espace personnel ni aux services associés.
</p>
<p>
Si vous n’êtes pas à l’origine de cette action, merci de nous contacter rapidement.
</p>
<p>
Si vous souhaitez à nouveau participer aux activités, vous pouvez effectuer une nouvelle inscription à tout moment.
</p>
<p>
À bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Validation de changement d'adresse mail</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>Votre adresse email a bien été mise à jour vers {{ $newEmail }}.</p>
<p>Si vous n'êtes pas à l'origine de ce changement, merci de contacter l’équipe du Comité des fêtes de Beaupont.</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Événement supprimé</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>
L’événement {{ $eventName }} a été supprimé par un administrateur.
Vous n’y participez donc plus. Cet événement avait lieu du {{ $start }} au {{ $end }}.
</p>
<p>
Si vous pensez qu’il s’agit d’une erreur ou si vous souhaitez plus d’informations,
merci de contacter l’équipe du Comité des fêtes de Beaupont.
</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demande de changement d'adresse mail</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>Une demande de changement d'adresse mail à été soumise.</p>
<p>Si vous n'êtes pas à l'origine de cette demande, veuillez contacter l'équipe du Comité des fêtes de Beaupont.</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
+38
View File
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nouvelle demande de validation</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $adminName }} {{ $adminLastname }},</p>
<p>
L’utilisateur {{ $userName }} {{ $userLastname }} a créé un compte
qui est maintenant en attente de validation sur le site du Comité des fêtes de Beaupont.
</p>
<p style="text-align: center; margin: 30px 0;">
<a href="{{ url('/admin') }}"
style="background-color: #f39c12; color: white; padding: 12px 20px; text-decoration: none; border-radius: 5px;">
Valider ce compte
</a>
</p>
<p>
Vous pouvez cliquer sur le bouton ci-dessus pour vous rendre sur la page permettant de valider le compte.
</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demande de changement d'adresse mail</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>Vous avez demandé à changer votre adresse email. Cliquez sur le bouton ci-dessous pour confirmer ce changement.</p>
<p style="text-align: center; margin: 30px 0;">
<a href="{{ config('vite_api_url') }}/confirm/email/change?token={{ $token }}"
style="background-color: #f39c12; color: white; padding: 12px 20px; text-decoration: none; border-radius: 5px;">
Confirmer le changement d'email
</a>
</p>
<p>Ce lien expire dans 1 heure. Si vous n'êtes pas à l'origine de cette demande, ignorez ce mail.</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Date de tâche modifiée</title>
</head>
<body style="padding: 20px; font-family: Arial, sans-serif;">
<div>
<p>Bonjour {{ $name }} {{ $lastname }},</p>
<p>
La date de la tâche {{ $taskName }} de l'événement {{ $eventName }} a été modifié par un administrateur.
Cette tâche avait lieu du {{ $oldStart }} au {{ $oldEnd }}. Et aura maintenant lieu du {{ $newStart }} au {{ $newEnd }}.
</p>
<p>
Si vous pensez qu’il s’agit d’une erreur ou si vous souhaitez plus d’informations,
merci de contacter l’équipe du Comité des fêtes de Beaupont.
</p>
<p>
À très bientôt,<br>
L’équipe du Comité des fêtes de Beaupont
</p>
</div>
<p style="font-size: 14px; color: #999; text-align: center; padding-top: 20px;">
Cet email a été envoyé automatiquement, merci de ne pas y répondre.
</p>
</body>
</html>
Loaded 100 of 421 files, more files were not shown because too many files have changed in this diff. Show more