External Services Setup
External Services Setup
Section titled “External Services Setup”This guide covers the setup and configuration of all external/3rd party services required for the Iran Nation project, including Firebase, Google Services, iOS integrations, monitoring, email services, and cloud storage.
Service Overview
Section titled “Service Overview”| Service | Purpose | Used By | Environment |
|---|---|---|---|
| Firebase | Authentication, Push Notifications, Analytics | Mobile, Backend | All |
| Google Services | OAuth, Maps, Cloud APIs | Mobile, Backend | All |
| Apple Developer | iOS App Store, Push Notifications | Mobile (iOS) | Production |
| Expo Services | EAS Build, Updates, Notifications | Mobile | All |
| Sentry | Error Tracking & Performance | Mobile, Backend, Web | All |
| Resend | Transactional Email | Backend | All |
| AWS S3 | File Storage & CDN | Backend | All |
| Stripe | Payment Processing | Mobile, Backend | Production |
Firebase Setup
Section titled “Firebase Setup”Firebase provides authentication, push notifications, analytics, and real-time database services.
1. Firebase Project Creation
Section titled “1. Firebase Project Creation”# Install Firebase CLInpm install -g firebase-tools
# Login to Firebasefirebase login
# Create new project (or use existing)firebase projects:create iran-nation-app2. Firebase Configuration
Section titled “2. Firebase Configuration”For Mobile App (iOS & Android)
Section titled “For Mobile App (iOS & Android)”-
Create iOS App in Firebase Console:
- Bundle ID:
com.irannetworkstate.app - Download
GoogleService-Info.plist - Place in
PersiaNation/ios/PersiaNation/
- Bundle ID:
-
Create Android App in Firebase Console:
- Package name:
com.irannetworkstate.app - Download
google-services.json - Place in
PersiaNation/android/app/
- Package name:
-
Configure Expo App:
export default { plugins: [ [ "@react-native-firebase/app", { // Firebase configuration will be auto-detected }, ], [ "@react-native-firebase/messaging", { // Enable push notifications ios: { requestPermission: true, }, }, ], ], ios: { googleServicesFile: "./GoogleService-Info.plist", }, android: { googleServicesFile: "./google-services.json", },};For Backend (NestJS)
Section titled “For Backend (NestJS)”# Install Firebase Admin SDKnpm install firebase-admin
# Generate service account key from Firebase Console# Download JSON file and save as firebase-credentials.jsonimport { Injectable } from "@nestjs/common";import * as admin from "firebase-admin";import { ConfigService } from "@nestjs/config";
@Injectable()export class FirebaseService { private app: admin.app.App;
constructor(private configService: ConfigService) { const serviceAccount = require("../../firebase-credentials.json");
this.app = admin.initializeApp({ credential: admin.credential.cert(serviceAccount), projectId: this.configService.get("FIREBASE_PROJECT_ID"), }); }
async sendNotification(token: string, payload: any) { return await admin.messaging().send({ token, notification: payload.notification, data: payload.data, }); }
async verifyIdToken(idToken: string) { return await admin.auth().verifyIdToken(idToken); }}3. Firebase Environment Configuration
Section titled “3. Firebase Environment Configuration”# .env filesFIREBASE_PROJECT_ID=iran-nation-appFIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxx@iran-nation-app.iam.gserviceaccount.comFIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"4. Firebase Security Rules
Section titled “4. Firebase Security Rules”// Firestore Security Rulesrules_version = '2';service cloud.firestore { match /databases/{database}/documents { // Users can only access their own data match /users/{userId} { allow read, write: if request.auth != null && request.auth.uid == userId; }
// Public read for announcements match /announcements/{document} { allow read: if true; allow write: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin'; } }}Google Services Setup
Section titled “Google Services Setup”1. Google Cloud Console Setup
Section titled “1. Google Cloud Console Setup”-
Create Google Cloud Project:
- Go to Google Cloud Console
- Create new project:
iran-nation-services - Enable billing for the project
-
Enable Required APIs:
# Enable APIs via CLIgcloud services enable gmail.googleapis.comgcloud services enable drive.googleapis.comgcloud services enable maps-backend.googleapis.comgcloud services enable places-backend.googleapis.comgcloud services enable geocoding-backend.googleapis.com2. Service Account for Backend
Section titled “2. Service Account for Backend”# Create service accountgcloud iam service-accounts create iran-nation-backend \ --description="Backend service account" \ --display-name="Iran Nation Backend"
# Create and download keygcloud iam service-accounts keys create ./google-service-account.json \ --iam-account=iran-nation-backend@iran-nation-services.iam.gserviceaccount.com3. OAuth 2.0 Configuration
Section titled “3. OAuth 2.0 Configuration”For Mobile App
Section titled “For Mobile App”-
Create OAuth Client ID:
- Application type: iOS/Android
- Bundle ID:
com.irannetworkstate.app - Download configuration files
-
Configure Expo:
export default { plugins: [ [ "@react-native-google-signin/google-signin", { webClientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com", }, ], ],};For Web App
Section titled “For Web App”// Web OAuth configurationconst googleAuthConfig = { clientId: "YOUR_WEB_CLIENT_ID.apps.googleusercontent.com", redirectUri: "https://yourapp.com/auth/google/callback", scopes: ["openid", "profile", "email"],};4. Google Maps Integration
Section titled “4. Google Maps Integration”// Mobile app configurationexport default { plugins: [ [ "react-native-maps", { googleMapsApiKey: process.env.GOOGLE_MAPS_API_KEY, }, ], ],};// Backend configurationimport { Injectable } from "@nestjs/common";import { ConfigService } from "@nestjs/config";
@Injectable()export class MapsService { constructor(private configService: ConfigService) {}
async geocode(address: string) { const apiKey = this.configService.get("GOOGLE_MAPS_API_KEY"); const response = await fetch( `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent( address )}&key=${apiKey}` ); return response.json(); }}iOS Integration Setup
Section titled “iOS Integration Setup”1. Apple Developer Account
Section titled “1. Apple Developer Account”- Enroll in Apple Developer Program: $99/year
- Create App Identifier:
- Bundle ID:
com.irannetworkstate.app - Enable capabilities: Push Notifications, Sign in with Apple
- Bundle ID:
2. Push Notifications Setup
Section titled “2. Push Notifications Setup”-
Create APNs Key:
- Go to Certificates, Identifiers & Profiles
- Create new Key with APNs enabled
- Download
.p8file
-
Configure in Firebase:
- Upload APNs key to Firebase Console
- Set Key ID and Team ID
3. App Store Connect Setup
Section titled “3. App Store Connect Setup”# Install App Store Connect API key# Download API key from App Store Connect# Configure in CI/CD pipeline
# EAS configurationeas secret:create --scope project --name APPLE_API_KEY --value "$(cat AuthKey_XXXXXXXXXX.p8)"eas secret:create --scope project --name APPLE_API_KEY_ID --value "XXXXXXXXXX"eas secret:create --scope project --name APPLE_API_ISSUER_ID --value "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"4. Sign in with Apple
Section titled “4. Sign in with Apple”// Mobile app configurationimport { AppleButton } from "@invertase/react-native-apple-authentication";
const signInWithApple = async () => { const appleAuthRequestResponse = await appleAuth.performRequest({ requestedOperation: appleAuth.Operation.LOGIN, requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME], });
// Send to backend for verification const response = await api.post("/auth/apple", { identityToken: appleAuthRequestResponse.identityToken, authorizationCode: appleAuthRequestResponse.authorizationCode, });};Expo Services Setup
Section titled “Expo Services Setup”1. Expo Account Configuration
Section titled “1. Expo Account Configuration”# Install EAS CLInpm install -g @expo/cli eas-cli
# Login to Expoexpo login
# Configure projecteas init --id your-project-id2. EAS Build Configuration
Section titled “2. EAS Build Configuration”{ "cli": { "version": ">= 5.0.0", "appVersionSource": "local" }, "build": { "production": { "channel": "production", "env": { "EXPO_PUBLIC_API_URL": "https://api.irannetworkstate.com", "EXPO_PUBLIC_ENVIRONMENT": "production" } }, "staging": { "channel": "staging", "env": { "EXPO_PUBLIC_API_URL": "https://staging-api.irannetworkstate.com", "EXPO_PUBLIC_ENVIRONMENT": "staging" } } }, "submit": { "production": { "ios": { "appleId": "your-apple-id@email.com", "ascAppId": "1234567890", "appleTeamId": "XXXXXXXXXX" }, "android": { "serviceAccountKeyPath": "./google-play-service-account.json", "track": "production" } } }}3. Expo Push Notifications
Section titled “3. Expo Push Notifications”import { Injectable } from "@nestjs/common";import { Expo, ExpoPushMessage } from "expo-server-sdk";
@Injectable()export class ExpoNotificationsService { private expo = new Expo({ accessToken: process.env.EXPO_ACCESS_TOKEN, });
async sendPushNotification(pushToken: string, message: string, data?: any) { const messages: ExpoPushMessage[] = [ { to: pushToken, sound: "default", body: message, data, }, ];
const chunks = this.expo.chunkPushNotifications(messages); const tickets = [];
for (const chunk of chunks) { const ticketChunk = await this.expo.sendPushNotificationsAsync(chunk); tickets.push(...ticketChunk); }
return tickets; }}Sentry Setup
Section titled “Sentry Setup”Sentry provides error tracking and performance monitoring across all platforms.
1. Sentry Project Setup
Section titled “1. Sentry Project Setup”# Install Sentry CLInpm install -g @sentry/cli
# Create Sentry account and project# Get DSN from Sentry dashboard2. Mobile App Integration
Section titled “2. Mobile App Integration”# Install Sentry for React Nativecd PersiaNationnpx @sentry/react-native installexport default { plugins: [ [ "@sentry/react-native/expo", { organization: "iran-network-state", project: "persia-nation-mobile", authToken: process.env.SENTRY_AUTH_TOKEN, }, ], ],};import * as Sentry from "@sentry/react-native";
Sentry.init({ dsn: "https://your-dsn@sentry.io/project-id", environment: process.env.EXPO_PUBLIC_ENVIRONMENT, enableAutoSessionTracking: true, sessionTrackingIntervalMillis: 10000,});
export default Sentry;3. Backend Integration
Section titled “3. Backend Integration”# Install Sentry for NestJScd ins-nestnpm install @sentry/node @sentry/tracingimport * as Sentry from "@sentry/node";import "@sentry/tracing";
Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0,});
// Add Sentry request handlerapp.use(Sentry.Handlers.requestHandler());app.use(Sentry.Handlers.tracingHandler());
// Add error handler before other error middlewareapp.use(Sentry.Handlers.errorHandler());4. Web App Integration
Section titled “4. Web App Integration”import * as Sentry from "@sentry/astro";
Sentry.init({ dsn: "https://your-dsn@sentry.io/project-id", environment: import.meta.env.MODE, integrations: [new Sentry.BrowserTracing()], tracesSampleRate: 1.0,});Resend Email Service
Section titled “Resend Email Service”1. Resend Setup
Section titled “1. Resend Setup”# Sign up at resend.com# Get API key from dashboard# Verify domain for production use2. Backend Integration
Section titled “2. Backend Integration”# Install Resend SDKnpm install resendimport { Injectable } from "@nestjs/common";import { Resend } from "resend";import { ConfigService } from "@nestjs/config";
@Injectable()export class ResendService { private resend: Resend;
constructor(private configService: ConfigService) { this.resend = new Resend(this.configService.get("RESEND_API_KEY")); }
async sendWelcomeEmail(to: string, name: string) { return await this.resend.emails.send({ from: "Iran Nation <noreply@irannetworkstate.com>", to, subject: "Welcome to Iran Nation", html: ` <h1>Welcome ${name}!</h1> <p>Thank you for joining Iran Nation.</p> `, }); }
async sendPasswordReset(to: string, resetToken: string) { const resetUrl = `${this.configService.get( "FRONTEND_URL" )}/reset-password?token=${resetToken}`;
return await this.resend.emails.send({ from: "Iran Nation <noreply@irannetworkstate.com>", to, subject: "Reset Your Password", html: ` <h1>Password Reset Request</h1> <p>Click the link below to reset your password:</p> <a href="${resetUrl}">Reset Password</a> <p>This link expires in 1 hour.</p> `, }); }}3. Email Templates
Section titled “3. Email Templates”export const welcomeEmailTemplate = (name: string, verificationUrl: string) => `<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Welcome to Iran Nation</title> <style> body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; } .container { max-width: 600px; margin: 0 auto; padding: 20px; } .header { text-align: center; margin-bottom: 30px; } .button { display: inline-block; padding: 12px 24px; background-color: #5d4037; color: white; text-decoration: none; border-radius: 5px; } </style></head><body> <div class="container"> <div class="header"> <h1>Welcome to Iran Nation, ${name}!</h1> </div>
<p>Thank you for joining our community. To get started, please verify your email address:</p>
<p style="text-align: center;"> <a href="${verificationUrl}" class="button">Verify Email Address</a> </p>
<p>If you didn't create an account, you can safely ignore this email.</p>
<p>Best regards,<br>The Iran Nation Team</p> </div></body></html>`;AWS S3 Setup
Section titled “AWS S3 Setup”1. AWS Account Setup
Section titled “1. AWS Account Setup”# Install AWS CLIaws configure# Enter Access Key ID, Secret Access Key, Region, Output format
# Create S3 bucketaws s3 mb s3://iran-nation-storage --region us-east-1
# Set bucket policy for public read access (for public assets)aws s3api put-bucket-policy --bucket iran-nation-storage --policy file://bucket-policy.json2. Bucket Policy Configuration
Section titled “2. Bucket Policy Configuration”{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::iran-nation-storage/public/*" } ]}3. Backend Integration
Section titled “3. Backend Integration”# Install AWS SDKnpm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner multer-s3import { Injectable } from "@nestjs/common";import { S3Client, PutObjectCommand, DeleteObjectCommand,} from "@aws-sdk/client-s3";import { getSignedUrl } from "@aws-sdk/s3-request-presigner";import { ConfigService } from "@nestjs/config";
@Injectable()export class S3Service { private s3Client: S3Client; private bucketName: string;
constructor(private configService: ConfigService) { this.s3Client = new S3Client({ region: this.configService.get("AWS_REGION"), credentials: { accessKeyId: this.configService.get("AWS_ACCESS_KEY_ID"), secretAccessKey: this.configService.get("AWS_SECRET_ACCESS_KEY"), }, }); this.bucketName = this.configService.get("AWS_S3_BUCKET"); }
async uploadFile(key: string, file: Buffer, contentType: string) { const command = new PutObjectCommand({ Bucket: this.bucketName, Key: key, Body: file, ContentType: contentType, });
await this.s3Client.send(command); return `https://${this.bucketName}.s3.amazonaws.com/${key}`; }
async getSignedUploadUrl(key: string, contentType: string, expiresIn = 3600) { const command = new PutObjectCommand({ Bucket: this.bucketName, Key: key, ContentType: contentType, });
return await getSignedUrl(this.s3Client, command, { expiresIn }); }
async deleteFile(key: string) { const command = new DeleteObjectCommand({ Bucket: this.bucketName, Key: key, });
await this.s3Client.send(command); }}4. File Upload Controller
Section titled “4. File Upload Controller”import { Controller, Post, UseInterceptors, UploadedFile, Body,} from "@nestjs/common";import { FileInterceptor } from "@nestjs/platform-express";import { S3Service } from "./s3.service";
@Controller("files")export class FileStorageController { constructor(private s3Service: S3Service) {}
@Post("upload") @UseInterceptors(FileInterceptor("file")) async uploadFile(@UploadedFile() file: Express.Multer.File) { const key = `uploads/${Date.now()}-${file.originalname}`; const url = await this.s3Service.uploadFile( key, file.buffer, file.mimetype );
return { url, key }; }
@Post("signed-url") async getSignedUploadUrl( @Body() { filename, contentType }: { filename: string; contentType: string } ) { const key = `uploads/${Date.now()}-${filename}`; const signedUrl = await this.s3Service.getSignedUploadUrl(key, contentType);
return { signedUrl, key }; }}Stripe Payment Integration
Section titled “Stripe Payment Integration”1. Stripe Account Setup
Section titled “1. Stripe Account Setup”# Sign up at stripe.com# Get publishable and secret keys from dashboard# Configure webhooks endpoint2. Backend Integration
Section titled “2. Backend Integration”# Install Stripe SDKnpm install stripeimport { Injectable } from "@nestjs/common";import Stripe from "stripe";import { ConfigService } from "@nestjs/config";
@Injectable()export class StripeService { private stripe: Stripe;
constructor(private configService: ConfigService) { this.stripe = new Stripe(this.configService.get("STRIPE_SECRET_KEY"), { apiVersion: "2023-10-16", }); }
async createPaymentIntent( amount: number, currency = "usd", customerId?: string ) { return await this.stripe.paymentIntents.create({ amount: amount * 100, // Convert to cents currency, customer: customerId, automatic_payment_methods: { enabled: true, }, }); }
async createCustomer(email: string, name: string) { return await this.stripe.customers.create({ email, name, }); }
async handleWebhook(payload: string, signature: string) { const endpointSecret = this.configService.get("STRIPE_WEBHOOK_SECRET");
try { const event = this.stripe.webhooks.constructEvent( payload, signature, endpointSecret );
switch (event.type) { case "payment_intent.succeeded": // Handle successful payment break; case "payment_intent.payment_failed": // Handle failed payment break; default: console.log(`Unhandled event type ${event.type}`); }
return { received: true }; } catch (err) { throw new Error(`Webhook signature verification failed: ${err.message}`); } }}3. Mobile Integration
Section titled “3. Mobile Integration”# Install Stripe React Native SDKcd PersiaNationnpm install @stripe/stripe-react-nativeimport { StripeProvider, useStripe } from "@stripe/stripe-react-native";
export const StripeWrapper = ({ children }) => ( <StripeProvider publishableKey="pk_test_..." merchantIdentifier="merchant.com.irannetworkstate.app" > {children} </StripeProvider>);
// Payment componentexport const PaymentScreen = () => { const { initPaymentSheet, presentPaymentSheet } = useStripe();
const initializePaymentSheet = async () => { const { paymentIntent } = await fetch("/api/payment-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ amount: 2000 }), // $20.00 }).then((res) => res.json());
const { error } = await initPaymentSheet({ merchantDisplayName: "Iran Nation", paymentIntentClientSecret: paymentIntent.client_secret, });
if (error) { console.error("Error initializing payment sheet:", error); } };
const handlePayment = async () => { const { error } = await presentPaymentSheet();
if (error) { console.error("Payment failed:", error); } else { console.log("Payment succeeded!"); } };};Environment Variables Management
Section titled “Environment Variables Management”1. Environment Configuration Files
Section titled “1. Environment Configuration Files”# Development (.env.development)NODE_ENV=developmentDATABASE_URL=postgresql://localhost:5432/iran_nation_devREDIS_URL=redis://localhost:6379
# FirebaseFIREBASE_PROJECT_ID=iran-nation-devFIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxx@iran-nation-dev.iam.gserviceaccount.comFIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# Google ServicesGOOGLE_MAPS_API_KEY=AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXGOOGLE_OAUTH_CLIENT_ID=xxxxx.apps.googleusercontent.comGOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxxx
# Apple ServicesAPPLE_TEAM_ID=XXXXXXXXXXAPPLE_KEY_ID=XXXXXXXXXXAPPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# ExpoEXPO_ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# SentrySENTRY_DSN=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxx@sentry.io/xxxxxxxSENTRY_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Email (Resend)RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# AWS S3AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXXAWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAWS_REGION=us-east-1AWS_S3_BUCKET=iran-nation-storage
# StripeSTRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxSTRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxSTRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# URLsFRONTEND_URL=http://localhost:3000BACKEND_URL=http://localhost:30012. Production Environment Setup
Section titled “2. Production Environment Setup”# Production (.env.production)NODE_ENV=productionDATABASE_URL=postgresql://prod-db:5432/iran_nationREDIS_URL=redis://prod-redis:6379
# Use production keys for all servicesFIREBASE_PROJECT_ID=iran-nation-prodGOOGLE_MAPS_API_KEY=AIzaSyPROD_KEY_HERESENTRY_DSN=https://prod-dsn@sentry.io/xxxxxxxRESEND_API_KEY=re_PROD_KEY_HEREAWS_S3_BUCKET=iran-nation-prod-storageSTRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Production URLsFRONTEND_URL=https://irannetworkstate.comBACKEND_URL=https://api.irannetworkstate.com3. CI/CD Secrets Management
Section titled “3. CI/CD Secrets Management”# GitHub Secrets (for Actions)gh secret set FIREBASE_PRIVATE_KEY --body "$(cat firebase-private-key.txt)"gh secret set GOOGLE_SERVICES_JSON --body "$(cat google-services.json)"gh secret set APPLE_PRIVATE_KEY --body "$(cat AuthKey_XXXXXXXXXX.p8)"gh secret set EXPO_TOKEN --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"gh secret set SENTRY_AUTH_TOKEN --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"gh secret set AWS_ACCESS_KEY_ID --body "AKIAXXXXXXXXXXXXXXXX"gh secret set AWS_SECRET_ACCESS_KEY --body "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"gh secret set STRIPE_SECRET_KEY --body "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"gh secret set RESEND_API_KEY --body "re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"Security Best Practices
Section titled “Security Best Practices”1. API Key Security
Section titled “1. API Key Security”- Never commit keys to git: Use
.envfiles and.gitignore - Rotate keys regularly: Set up key rotation schedules
- Use environment-specific keys: Separate dev/staging/prod keys
- Implement key restrictions: Limit API keys to specific IPs/domains
2. Service Account Permissions
Section titled “2. Service Account Permissions”# Principle of least privilege# Only grant necessary permissions to each service account
# Example: S3 bucket policy for specific operations only{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::iran-nation-storage/uploads/*" } ]}3. Network Security
Section titled “3. Network Security”- Use HTTPS everywhere: All API endpoints and webhooks
- Implement rate limiting: Prevent abuse of external services
- Validate webhooks: Verify signatures from external services
- Monitor usage: Set up alerts for unusual activity
Monitoring & Maintenance
Section titled “Monitoring & Maintenance”1. Service Health Monitoring
Section titled “1. Service Health Monitoring”import { Injectable } from "@nestjs/common";import { HealthIndicator, HealthIndicatorResult } from "@nestjs/terminus";
@Injectable()export class ExternalServicesHealthIndicator extends HealthIndicator { async checkFirebase(): Promise<HealthIndicatorResult> { try { // Test Firebase connection await admin.auth().listUsers(1); return this.getStatus("firebase", true); } catch (error) { return this.getStatus("firebase", false, { error: error.message }); } }
async checkS3(): Promise<HealthIndicatorResult> { try { // Test S3 connection await this.s3Client.send( new HeadBucketCommand({ Bucket: this.bucketName }) ); return this.getStatus("s3", true); } catch (error) { return this.getStatus("s3", false, { error: error.message }); } }
async checkStripe(): Promise<HealthIndicatorResult> { try { // Test Stripe connection await this.stripe.balance.retrieve(); return this.getStatus("stripe", true); } catch (error) { return this.getStatus("stripe", false, { error: error.message }); } }}2. Cost Monitoring
Section titled “2. Cost Monitoring”- Set up billing alerts: Monitor service usage costs
- Implement usage quotas: Prevent unexpected charges
- Regular cost reviews: Analyze service usage patterns
- Optimize configurations: Adjust settings based on usage
3. Service Updates & Maintenance
Section titled “3. Service Updates & Maintenance”- Monitor service announcements: Stay updated on API changes
- Test in staging first: Always test service updates in non-prod
- Maintain fallbacks: Implement graceful degradation
- Document dependencies: Keep track of service versions and configs
Troubleshooting Common Issues
Section titled “Troubleshooting Common Issues”Firebase Issues
Section titled “Firebase Issues”# Common Firebase problems and solutions
# 1. Authentication token expiredfirebase login --reauth
# 2. Permission denied errors# Check Firebase security rules and user permissions
# 3. Push notification delivery issues# Verify APNs certificates and FCM server keyGoogle Services Issues
Section titled “Google Services Issues”# 1. API quota exceeded# Check Google Cloud Console quotas and increase if needed
# 2. OAuth consent screen issues# Verify domain ownership and consent screen configuration
# 3. Maps API not working# Check API key restrictions and enabled APIsAWS S3 Issues
Section titled “AWS S3 Issues”# 1. Access denied errorsaws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::account:user/username \ --action-names s3:GetObject \ --resource-arns arn:aws:s3:::bucket-name/key-name
# 2. CORS issuesaws s3api put-bucket-cors --bucket bucket-name --cors-configuration file://cors.jsonStripe Issues
Section titled “Stripe Issues”# 1. Webhook signature verification failed# Verify webhook endpoint URL and secret key
# 2. Payment method declined# Check Stripe dashboard for detailed error messages
# 3. Test vs live mode confusion# Ensure you're using the correct API keys for the environmentRelated Documentation
Section titled “Related Documentation”- Mobile App Installation: Mobile development setup
- Backend Installation: Backend development setup
- Mobile CI/CD Workflows: Automation and deployment
- Mobile Release Integration: Release coordination
This comprehensive guide covers all the external services needed for the Iran Nation project. Each service should be configured according to your specific requirements and security policies. Remember to regularly review and update configurations as services evolve and new features are added to the application.