I4 — OTP على السحب عبر نبيه. تدفّق خطوتين، وقاعدته: **لا يتحرك مال قبل إثبات الهوية**: - POST /payouts/request → يرسل رمزاً، حالة pending_otp، **بلا خصم** - POST /payouts/:id/confirm → يتحقق ثم يحجز ذرّياً (خصم I1) - حدّ 5 محاولات (رمز 4 خانات يُخمَّن في دقائق بلا حدّ)، الرمز يُستهلك مرة - complete يرفض طلباً بلا otp_verified_at (حارس ضد تخطّي التأكيد) - fail لا يردّ مالاً لطلب pending_otp — لم يُخصم منه شيء، وردّه يخلق مالاً I5 — biometric_method/at + device_id + request_ip على السحب. أثرٌ للتحقيق لا مصادقة: العميل يستطيع ادّعاءها، والمصادقة الحقيقية JWT + رمز واتساب. I6 — HMAC **مبنيّ ومطفأ** (PAYMENTS_REQUIRE_SIGNATURE=false) حتى يوقّع فلاتر؛ تفعيله الآن يقطع كل سحب. مفتاح **لكل جلسة** يُصدره الدخول لا سرّ ثابت في التطبيق (الثابت يُستخرج بالهندسة العكسية فيصير التوقيع مسرحية). يوقّع timestamp.METHOD.path.body بنافذة 5 دقائق؛ rawBody مفعّل في main. I7 — tripz_audit_log append-only: من·ماذا·متى·أي IP وجهاز. لا يرمي أبداً — فشل التدقيق يجب ألّا يُسقط عمليةً مالية نجحت. I2 أُلغيت الحاجة إليها: إيراد المنصة = الشحن، و credit_txns دفتره فعلاً؛ محفظة ثانية = دفتر مزدوج يحتاج مطابقة. الباقي تقرير لا محفظة. I3/I8 مؤجَّلتان بوعي (توثيق في docs/17). wallet-race-test.mjs حُدِّث: السباق انتقل من request إلى confirm، وأُضيف محكّ أن الطلب وحده لا يمسّ الرصيد. هجرة: PayoutSecurityAndAudit (تعتبر السحوبات القائمة مُتحقَّقة وإلا رفض complete صرفها للأبد). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
4.7 KiB
TypeScript
104 lines
4.7 KiB
TypeScript
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
import { ThrottlerModule } from '@nestjs/throttler';
|
|
import configuration from './config/configuration';
|
|
import { TenantMiddleware } from './common/tenant/tenant.middleware';
|
|
import { RedisModule } from './common/redis/redis.module';
|
|
import { I18nModule } from './common/i18n/i18n.module';
|
|
import { CacheModule } from './common/cache/cache.module';
|
|
import { EntitlementsModule } from './common/entitlements/entitlements.module';
|
|
import { PlatformModule } from './common/platform/platform.module';
|
|
import { AuditModule } from './common/audit/audit.module';
|
|
import { SigningModule } from './common/signing/signing.module';
|
|
import { SeedModule } from './common/seed/seed.module';
|
|
import { HealthModule } from './modules/health/health.module';
|
|
import { TenantsModule } from './modules/tenants/tenants.module';
|
|
import { UsersModule } from './modules/users/users.module';
|
|
import { AuthModule } from './modules/auth/auth.module';
|
|
import { DriversModule } from './modules/drivers/drivers.module';
|
|
import { MatchingModule } from './modules/matching/matching.module';
|
|
import { LocationsModule } from './modules/locations/locations.module';
|
|
import { TariffModule } from './modules/tariff/tariff.module';
|
|
import { MapsModule } from './modules/maps/maps.module';
|
|
import { TripsModule } from './modules/trips/trips.module';
|
|
import { RealtimeModule } from './realtime/realtime.module';
|
|
import { FraudModule } from './modules/fraud/fraud.module';
|
|
import { ChatModule } from './modules/chat/chat.module';
|
|
import { RatingsModule } from './modules/ratings/ratings.module';
|
|
import { NabehModule } from './integrations/nabeh/nabeh.module';
|
|
import { RideTypesModule } from './modules/ride-types/ride-types.module';
|
|
import { DispatchModule } from './modules/dispatch/dispatch.module';
|
|
import { WalletModule } from './modules/wallet/wallet.module';
|
|
import { CreditModule } from './modules/credit/credit.module';
|
|
import { NotificationsModule } from './modules/notifications/notifications.module';
|
|
import { PaymentsModule } from './modules/payments/payments.module';
|
|
import { StorageModule } from './common/storage/storage.module';
|
|
import { DocumentsModule } from './modules/documents/documents.module';
|
|
import { GeminiModule } from './integrations/gemini/gemini.module';
|
|
|
|
@Module({
|
|
imports: [
|
|
ConfigModule.forRoot({ isGlobal: true, load: [configuration] }),
|
|
|
|
TypeOrmModule.forRootAsync({
|
|
inject: [ConfigService],
|
|
useFactory: (cfg: ConfigService) => ({
|
|
type: 'postgres',
|
|
host: cfg.get<string>('db.host'),
|
|
port: cfg.get<number>('db.port'),
|
|
database: cfg.get<string>('db.name'),
|
|
username: cfg.get<string>('db.user'),
|
|
password: cfg.get<string>('db.password'),
|
|
// بركة اتصالات أكبر — يرفع سقف التزامن تحت الحمل العالي
|
|
extra: { max: parseInt(process.env.DB_POOL_MAX ?? '30', 10) },
|
|
// بادئة الجداول (tripz_) لعزل السيرفر المشترك — راجع docs/14
|
|
entityPrefix: cfg.get<string>('db.tablePrefix'),
|
|
synchronize: cfg.get<boolean>('db.synchronize'),
|
|
autoLoadEntities: true,
|
|
}),
|
|
}),
|
|
|
|
ThrottlerModule.forRoot([{ ttl: 60000, limit: 120 }]),
|
|
|
|
RedisModule, // عالمي — عميل Redis للمطابقة و OTP
|
|
I18nModule, // عالمي — ترجمة نصوص الإشعارات
|
|
CacheModule, // عالمي — كاش-جانبي فوق Redis (خط أول قبل القاعدة)
|
|
EntitlementsModule, // عالمي — استحقاقات الاشتراك + FeatureGuard
|
|
PlatformModule, // عالمي — حارس السوبر-أدمن (x-platform-secret)
|
|
AuditModule, // عالمي — سجل التدقيق المالي
|
|
SigningModule, // عالمي — توقيع HMAC للعمليات المالية
|
|
NabehModule, // عالمي — إرسال OTP واتساب
|
|
NotificationsModule, // عالمي — FCM
|
|
StorageModule, // عالمي — تخزين ملفات الوثائق
|
|
GeminiModule, // عالمي — رؤية AI (وثائق + وجه)
|
|
|
|
HealthModule,
|
|
TenantsModule,
|
|
UsersModule,
|
|
AuthModule,
|
|
MapsModule,
|
|
MatchingModule,
|
|
LocationsModule,
|
|
TariffModule,
|
|
RideTypesModule,
|
|
DriversModule,
|
|
RealtimeModule,
|
|
FraudModule,
|
|
WalletModule,
|
|
CreditModule,
|
|
PaymentsModule,
|
|
TripsModule,
|
|
DispatchModule,
|
|
ChatModule,
|
|
RatingsModule,
|
|
DocumentsModule,
|
|
SeedModule,
|
|
],
|
|
})
|
|
export class AppModule implements NestModule {
|
|
configure(consumer: MiddlewareConsumer) {
|
|
consumer.apply(TenantMiddleware).forRoutes('*');
|
|
}
|
|
}
|