feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data. - Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS. - Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates. - Develop SendSmsHandler for handling SMS sending messages. - Create SmsService to manage SMS dispatching and logging. - Add UserProfileController for managing user profiles with CRUD operations. - Implement UserProfile entity and repository for user profile data management. - Update symfony.lock and bootstrap.php for project dependencies and environment setup.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
framework:
|
||||
cache:
|
||||
app: cache.adapter.redis
|
||||
default_redis_provider: '%env(REDIS_URL)%'
|
||||
@@ -0,0 +1,5 @@
|
||||
when@dev:
|
||||
debug:
|
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
|
||||
# See the "server:dump" command to start a new server.
|
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
|
||||
@@ -0,0 +1,45 @@
|
||||
doctrine:
|
||||
dbal:
|
||||
url: '%env(resolve:DATABASE_URL)%'
|
||||
profiling_collect_backtrace: '%kernel.debug%'
|
||||
use_savepoints: true
|
||||
orm:
|
||||
auto_generate_proxy_classes: true
|
||||
enable_lazy_ghost_objects: true
|
||||
report_fields_where_declared: true
|
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||
auto_mapping: true
|
||||
mappings:
|
||||
App:
|
||||
type: attribute
|
||||
is_bundle: false
|
||||
dir: '%kernel.project_dir%/src'
|
||||
prefix: 'App'
|
||||
alias: App
|
||||
controller_resolver:
|
||||
auto_mapping: false
|
||||
|
||||
when@test:
|
||||
doctrine:
|
||||
dbal:
|
||||
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
|
||||
|
||||
when@prod:
|
||||
doctrine:
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
|
||||
query_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.system_cache_pool
|
||||
result_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.result_cache_pool
|
||||
|
||||
framework:
|
||||
cache:
|
||||
pools:
|
||||
doctrine.result_cache_pool:
|
||||
adapter: cache.app
|
||||
doctrine.system_cache_pool:
|
||||
adapter: cache.system
|
||||
@@ -0,0 +1,6 @@
|
||||
doctrine_migrations:
|
||||
migrations_paths:
|
||||
# namespace is arbitrary but should be different from App\Migrations
|
||||
# as migrations classes should NOT be autoloaded
|
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations'
|
||||
enable_profiler: false
|
||||
@@ -0,0 +1,15 @@
|
||||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||
framework:
|
||||
secret: '%env(APP_SECRET)%'
|
||||
|
||||
# Note that the session will be started ONLY if you read or write from it.
|
||||
session: true
|
||||
|
||||
#esi: true
|
||||
#fragments: true
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
test: true
|
||||
session:
|
||||
storage_factory_id: session.storage.factory.mock_file
|
||||
@@ -0,0 +1,5 @@
|
||||
lexik_jwt_authentication:
|
||||
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
|
||||
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
|
||||
pass_phrase: '%env(JWT_PASSPHRASE)%'
|
||||
token_ttl: 3600
|
||||
@@ -0,0 +1,22 @@
|
||||
framework:
|
||||
messenger:
|
||||
failure_transport: failed
|
||||
|
||||
transports:
|
||||
async:
|
||||
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
||||
retry_strategy:
|
||||
max_retries: 3
|
||||
delay: 5000
|
||||
multiplier: 2
|
||||
failed: 'doctrine://default?queue_name=failed'
|
||||
sync: 'sync://'
|
||||
|
||||
routing:
|
||||
'App\Shared\Message\SendSmsMessage': async
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
messenger:
|
||||
transports:
|
||||
async: 'in-memory://'
|
||||
@@ -0,0 +1,19 @@
|
||||
nelmio_api_doc:
|
||||
documentation:
|
||||
info:
|
||||
title: ClinicPro API
|
||||
description: مستندات API سیستم کلینیکپرو
|
||||
version: 1.0.0
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
security:
|
||||
- bearerAuth: []
|
||||
areas:
|
||||
path_patterns:
|
||||
- ^/api
|
||||
- ^/oauth
|
||||
- ^/health
|
||||
@@ -0,0 +1,16 @@
|
||||
nelmio_cors:
|
||||
defaults:
|
||||
origin_regex: true
|
||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||
allow_methods: ['GET', 'OPTIONS', 'POST', 'PATCH', 'DELETE']
|
||||
allow_headers: ['Content-Type', 'Authorization', 'X-CSRF-Token', 'Content-Disposition']
|
||||
expose_headers: ['X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset']
|
||||
max_age: 3600
|
||||
allow_credentials: false
|
||||
paths:
|
||||
'^/api/':
|
||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||
'^/oauth/':
|
||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||
'^/health':
|
||||
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
|
||||
@@ -0,0 +1,3 @@
|
||||
framework:
|
||||
property_info:
|
||||
with_constructor_extractor: true
|
||||
@@ -0,0 +1,13 @@
|
||||
framework:
|
||||
rate_limiter:
|
||||
# OTP send-code: max 5 requests per hour per IP (prevents SMS flood)
|
||||
send_code:
|
||||
policy: 'sliding_window'
|
||||
limit: 5
|
||||
interval: '60 minutes'
|
||||
|
||||
# Login: max 10 attempts per minute per IP (brute force protection)
|
||||
login:
|
||||
policy: 'fixed_window'
|
||||
limit: 10
|
||||
interval: '1 minute'
|
||||
@@ -0,0 +1,10 @@
|
||||
framework:
|
||||
router:
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
default_uri: '%env(DEFAULT_URI)%'
|
||||
|
||||
when@prod:
|
||||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
||||
@@ -0,0 +1,79 @@
|
||||
security:
|
||||
password_hashers:
|
||||
App\Auth\Entity\User:
|
||||
algorithm: auto
|
||||
|
||||
providers:
|
||||
app_user_provider:
|
||||
entity:
|
||||
class: App\Auth\Entity\User
|
||||
property: mobileNumber
|
||||
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_profiler|_wdt|assets|build)/
|
||||
security: false
|
||||
|
||||
health:
|
||||
pattern: ^/health$
|
||||
security: false
|
||||
|
||||
public_endpoints:
|
||||
pattern: ^/(api/v1/user/(send-code|verify-code|register)|oauth/token$|session/token|api/v1/categorys/|api/v1/doctors$|api/v1/clinics$|api/v1/clinic/doctor-list/|api/v1/clinic-pro/doctor-addresses/|api/v1/appointment-slots|api/v1/comments/|api/v1/rate/|api/v1/blogs$)
|
||||
stateless: true
|
||||
security: false
|
||||
|
||||
payment_callback:
|
||||
pattern: ^/api/v1/(payment|subscription-payment)/callback/
|
||||
stateless: true
|
||||
security: false
|
||||
|
||||
api:
|
||||
pattern: ^/(api|oauth)/
|
||||
stateless: true
|
||||
provider: app_user_provider
|
||||
custom_authenticators:
|
||||
- App\Auth\Security\PasswordAuthenticator
|
||||
jwt: ~
|
||||
|
||||
access_control:
|
||||
- { path: ^/health$, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/user/send-code, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/user/verify-code, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/user/register, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/user/login, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/appointment-slots, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/comments/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/rate/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/blogs$, roles: PUBLIC_ACCESS }
|
||||
- path: '^/api/v1/blog/[^/]+$'
|
||||
methods: [GET]
|
||||
roles: PUBLIC_ACCESS
|
||||
- { path: ^/oauth/token$, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/oauth/token/refresh$, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/session/token, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/payment/callback/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/subscription-payment/callback/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/doc, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/categorys/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/doctors$, roles: PUBLIC_ACCESS }
|
||||
- path: '^/api/v1/doctor/[^/]+$'
|
||||
methods: [GET]
|
||||
roles: PUBLIC_ACCESS
|
||||
- { path: ^/api/v1/clinic/doctor-list/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/clinic-pro/doctor-addresses/, roles: PUBLIC_ACCESS }
|
||||
- { path: ^/api/v1/clinics$, roles: PUBLIC_ACCESS }
|
||||
- path: '^/api/v1/clinic/[^/]+$'
|
||||
methods: [GET]
|
||||
roles: PUBLIC_ACCESS
|
||||
- { path: ^/api/v1/user/\d+$, methods: [DELETE], roles: ROLE_ADMIN }
|
||||
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
|
||||
- { path: ^/oauth/userinfo, roles: IS_AUTHENTICATED_FULLY }
|
||||
- { path: ^/oauth/logout, roles: IS_AUTHENTICATED_FULLY }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
App\Auth\Entity\User:
|
||||
algorithm: auto
|
||||
cost: 4
|
||||
@@ -0,0 +1,11 @@
|
||||
framework:
|
||||
validation:
|
||||
# Enables validator auto-mapping support.
|
||||
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||
#auto_mapping:
|
||||
# App\Entity\: []
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
||||
Reference in New Issue
Block a user