diff --git a/GITHUB_SETUP.md b/GITHUB_SETUP.md new file mode 100644 index 0000000..f93a5cf --- /dev/null +++ b/GITHUB_SETUP.md @@ -0,0 +1,119 @@ +# GitHub Actions Setup Guide + +## Required GitHub Secrets + +Add these secrets to your GitHub repository (Settings > Secrets and variables > Actions): + +### AWS Credentials +``` +AWS_ACCESS_KEY_ID=your-aws-access-key +AWS_SECRET_ACCESS_KEY=your-aws-secret-key +``` + +### JWT Configuration (CRITICAL - Generate secure random strings) +``` +JWT_SECRET=your-super-secure-jwt-secret-key-here +JWT_SECRET_REFRESH=your-super-secure-refresh-secret-key-here +SECRET_KEY=your-general-encryption-secret-key-here +``` + +### Admin Configuration +``` +SUPER_ADMIN_EMAIL=admin@syncrow.ae +SUPER_ADMIN_PASSWORD=YourSecureAdminPassword123! +``` + +### Tuya IoT Configuration +``` +TUYA_ACCESS_ID=your-tuya-access-id +TUYA_ACCESS_KEY=your-tuya-access-key +TRUN_ON_TUYA_SOCKET=true-or-false +``` + +### Firebase Configuration +``` +FIREBASE_API_KEY=your-firebase-api-key +FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com +FIREBASE_PROJECT_ID=your-project-id +FIREBASE_STORAGE_BUCKET=your-project.appspot.com +FIREBASE_MESSAGING_SENDER_ID=your-sender-id +FIREBASE_APP_ID=your-app-id +FIREBASE_MEASUREMENT_ID=your-measurement-id +FIREBASE_DATABASE_URL=https://your-project.firebaseio.com +``` + +### Google OAuth +``` +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +``` + +### OneSignal Push Notifications +``` +ONESIGNAL_APP_ID=your-onesignal-app-id +ONESIGNAL_API_KEY=your-onesignal-api-key +``` + +### Email Configuration (SMTP) +``` +SMTP_HOST=your-smtp-host +SMTP_USER=your-smtp-username +SMTP_PASSWORD=your-smtp-password +``` + +### Mailtrap Configuration +``` +MAILTRAP_API_TOKEN=your-mailtrap-api-token +MAILTRAP_ENABLE_TEMPLATE_UUID=template-uuid +MAILTRAP_DISABLE_TEMPLATE_UUID=template-uuid +MAILTRAP_INVITATION_TEMPLATE_UUID=template-uuid +MAILTRAP_DELETE_USER_TEMPLATE_UUID=template-uuid +MAILTRAP_EDIT_USER_TEMPLATE_UUID=template-uuid +``` + +### Optional Services (leave empty if not used) +``` +AZURE_REDIS_CONNECTIONSTRING=your-redis-connection-string +DOPPLER_PROJECT=your-doppler-project +DOPPLER_CONFIG=your-doppler-config +DOPPLER_ENVIRONMENT=your-doppler-environment +ACCESS_KEY=your-access-key +DOCKER_REGISTRY_SERVER_URL=your-registry-url +DOCKER_REGISTRY_SERVER_USERNAME=your-registry-username +DOCKER_REGISTRY_SERVER_PASSWORD=your-registry-password +``` + +## Setup Steps + +1. **Add AWS Credentials** + - Create IAM user with ECR, ECS, CloudFormation permissions + - Add AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to GitHub Secrets + +2. **Generate JWT Secrets** + - Use a secure random string generator + - Make JWT_SECRET and JWT_SECRET_REFRESH different values + - Keep these values secure and never share them + +3. **Configure Services** + - Add secrets for each service you're using + - Leave unused services empty (they'll default to empty strings) + +4. **Test Deployment** + - Push to master/main branch + - Check GitHub Actions tab for deployment status + - Verify API is accessible at https://api.syncrow.me + +## Security Notes + +- Never commit secrets to the repository +- Use GitHub Secrets for all sensitive values +- Rotate secrets regularly +- Monitor GitHub Actions logs for any exposed values +- Database password is automatically managed by AWS Secrets Manager + +## Troubleshooting + +- Check GitHub Actions logs for deployment errors +- Verify all required secrets are set +- Ensure AWS credentials have sufficient permissions +- Check ECS service logs in CloudWatch for runtime errors diff --git a/README.md b/README.md index 67eb623..a9c7753 100644 --- a/README.md +++ b/README.md @@ -107,3 +107,29 @@ $ npm run test:cov | | Standby Node | | | | +------------------+----------------+ | +-----------------------------------------------------------------+ + +## CDK Deployment + +• Bootstrap CDK (first time only): npx cdk bootstrap aws://482311766496/me-central-1 +• List available stacks: npx cdk list +• Deploy infrastructure: npx cdk deploy --require-approval never +• View changes before deploy: npx cdk diff +• Generate CloudFormation template: npx cdk synth +• Destroy infrastructure: npx cdk destroy +• Environment variables are configured in infrastructure/stack.ts +• After code changes: build Docker image, push to ECR, force ECS deployment +• Database seeding happens automatically on first deployment with DB_SYNC=true +• Admin credentials: admin@syncrow.ae / YourSecureAdminPassword123! +• Production API: https://api.syncrow.me +• Health check: https://api.syncrow.me/health + +## GitHub Actions Deployment + +• Automatic deployment on push to master/main branch +• Configure GitHub Secrets (see GITHUB_SETUP.md for complete list) +• Required secrets: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, JWT_SECRET, JWT_SECRET_REFRESH +• Workflow builds Docker image, pushes to ECR, and deploys CDK stack +• Environment variables are passed securely via GitHub Secrets +• Manual deployment: Go to Actions tab and run "Deploy Backend to AWS" workflow +• Check deployment status in GitHub Actions tab +• Logs available in CloudWatch under /ecs/syncrow-backend log group diff --git a/cdk.context.json b/cdk.context.json index 2f75a86..13bd4e7 100644 --- a/cdk.context.json +++ b/cdk.context.json @@ -12,5 +12,14 @@ "hosted-zone:account=482311766496:domainName=syncrow.me:region=us-east-2": { "Id": "/hostedzone/Z02085662NLJECF4DGJV3", "Name": "syncrow.me." + }, + "availability-zones:account=482311766496:region=me-central-1": [ + "me-central-1a", + "me-central-1b", + "me-central-1c" + ], + "hosted-zone:account=482311766496:domainName=syncrow.me:region=me-central-1": { + "Id": "/hostedzone/Z02085662NLJECF4DGJV3", + "Name": "syncrow.me." } } diff --git a/deploy.sh b/deploy.sh index 424473b..f821a65 100755 --- a/deploy.sh +++ b/deploy.sh @@ -2,9 +2,9 @@ set -e ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) -REGION=${AWS_DEFAULT_REGION:-us-east-2} +REGION=${AWS_DEFAULT_REGION:-me-central-1} -echo "Deploying to account: $ACCOUNT_ID in region: $REGION" +npx cdk deploy SyncrowBackendStack --context certificateArn=arn:aws:acm:me-central-1:482311766496:certificate/bea1e2ae-84a1-414e-8dbf-4599397e7ed0 --require-approval never aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com @@ -12,6 +12,11 @@ docker build --platform=linux/amd64 -t syncrow-backend . docker tag syncrow-backend:latest $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/syncrow-backend:latest docker push $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/syncrow-backend:latest -npx cdk deploy SyncrowBackendStack --context certificateArn=arn:aws:acm:us-east-2:482311766496:certificate/1d9ca713-e71c-4c04-bd1d-cd473a966646 --require-approval never +SERVICE_ARN=$(aws ecs list-services --cluster syncrow-backend-cluster --query 'serviceArns[0]' --output text --region $REGION 2>/dev/null || echo "") -aws ecs update-service --cluster syncrow-backend-cluster --service $(aws ecs list-services --cluster syncrow-backend-cluster --query 'serviceArns[0]' --output text | cut -d'/' -f3) --force-new-deployment +if [ "$SERVICE_ARN" != "" ] && [ "$SERVICE_ARN" != "None" ]; then + SERVICE_NAME=$(echo $SERVICE_ARN | cut -d'/' -f3) + aws ecs update-service --cluster syncrow-backend-cluster --service $SERVICE_NAME --force-new-deployment --region $REGION +else + npx cdk deploy SyncrowBackendStack --context certificateArn=arn:aws:acm:me-central-1:482311766496:certificate/bea1e2ae-84a1-414e-8dbf-4599397e7ed0 --require-approval never +fi diff --git a/infrastructure/app.ts b/infrastructure/app.ts index a5ee666..3344b67 100644 --- a/infrastructure/app.ts +++ b/infrastructure/app.ts @@ -8,8 +8,8 @@ const app = new cdk.App(); new BackendStack(app, 'SyncrowBackendStack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, - region: process.env.CDK_DEFAULT_REGION, + region: 'me-central-1', }, databaseName: 'syncrow', - certificateArn: app.node.tryGetContext('certificateArn'), + certificateArn: 'arn:aws:acm:me-central-1:482311766496:certificate/bea1e2ae-84a1-414e-8dbf-4599397e7ed0', }); diff --git a/infrastructure/stack.ts b/infrastructure/stack.ts index ab35fee..ee9fa27 100644 --- a/infrastructure/stack.ts +++ b/infrastructure/stack.ts @@ -81,6 +81,13 @@ export class BackendStack extends cdk.Stack { 'Allow ECS to connect to PostgreSQL' ); + // Temporary access for admin IP + dbSecurityGroup.addIngressRule( + ec2.Peer.ipv4('216.126.231.231/32'), + ec2.Port.tcp(5432), + 'Temporary access from admin IP' + ); + // Allow HTTP/HTTPS traffic to ALB albSecurityGroup.addIngressRule( ec2.Peer.anyIpv4(), @@ -110,13 +117,20 @@ export class BackendStack extends cdk.Stack { removalPolicy: cdk.RemovalPolicy.DESTROY, }); - // ECR Repository for Docker images + // ECR Repository for Docker images - ensure it's in the correct region const ecrRepository = new ecr.Repository(this, 'SyncrowBackendRepo', { repositoryName: 'syncrow-backend', removalPolicy: cdk.RemovalPolicy.DESTROY, emptyOnDelete: true, }); + // Output the correct ECR URI for this region + new cdk.CfnOutput(this, 'EcrRepositoryUriRegional', { + value: ecrRepository.repositoryUri, + description: `ECR Repository URI in region ${this.region}`, + exportName: `${this.stackName}-EcrRepositoryUriRegional`, + }); + // ECS Cluster const cluster = new ecs.Cluster(this, 'SyncrowCluster', { vpc: this.vpc, @@ -151,9 +165,10 @@ export class BackendStack extends cdk.Stack { certificate: apiCertificate, protocol: elbv2.ApplicationProtocol.HTTPS, redirectHTTP: true, - taskImageOptions: { + taskImageOptions: { image: ecs.ContainerImage.fromEcrRepository(ecrRepository, 'latest'), containerPort: 3000, + enableLogging: true, environment: { // App settings NODE_ENV: process.env.NODE_ENV || 'production', @@ -173,7 +188,7 @@ export class BackendStack extends cdk.Stack { JWT_SECRET_REFRESH: process.env.JWT_SECRET_REFRESH || 'syncrow-refresh-secret-key-2025-production-environment-different-secure-string', JWT_EXPIRE_TIME: process.env.JWT_EXPIRE_TIME || '1h', JWT_EXPIRE_TIME_REFRESH: process.env.JWT_EXPIRE_TIME_REFRESH || '7d', - + // Firebase Configuration FIREBASE_API_KEY: process.env.FIREBASE_API_KEY || '', FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN || '', @@ -221,9 +236,9 @@ export class BackendStack extends cdk.Stack { OTP_LIMITER: process.env.OTP_LIMITER || '5', SECRET_KEY: process.env.SECRET_KEY || 'another-random-secret-key-for-general-encryption', ACCESS_KEY: process.env.ACCESS_KEY || '', - DB_SYNC: process.env.DB_SYNC || 'false', + DB_SYNC: process.env.DB_SYNC || 'txsrue', - // Redis (if used) + // Redis (used?) AZURE_REDIS_CONNECTIONSTRING: process.env.AZURE_REDIS_CONNECTIONSTRING || '', // Docker Registry (for deployment) @@ -285,9 +300,6 @@ export class BackendStack extends cdk.Stack { scaleOutCooldown: cdk.Duration.minutes(2), }); - // For now, let's update the web app to use HTTPS URL and handle the certificate warning - // In production, you'll add a proper SSL certificate for api.syncrow.ae - // Grant ECS task access to RDS credentials if (dbCluster.secret) { dbCluster.secret.grantRead(fargateService.taskDefinition.taskRole);