Adding pipeline

This commit is contained in:
Benoy Bose 2025-03-13 13:13:35 +05:30
parent cff02bcc03
commit 04077d95f4
6 changed files with 198 additions and 11 deletions

View File

@ -0,0 +1,83 @@
trigger:
- master
pool:
vmImage: "ubuntu-latest"
variables:
major: $(VERSION_MAJOR)
minor: $(VERSION_MINOR)
prefix: $[format('{0}.{1}', variables['major'], variables['minor'])]
patch: $[counter(variables['prefix'], 100)]
buildNumber: $(major).$(minor).$(patch)
steps:
- task: PowerShell@2
displayName: "Setting build version"
inputs:
targetType: "inline"
script: |
Write-Host "##vso[build.updatebuildnumber]${{ parameters.buildNumber }}"
- task: NodeTool@0
displayName: "Install Node"
inputs:
version: "20"
- task: Npm@1
displayName: "npm install"
inputs:
command: "install"
- task: PowerShell@2
displayName: "Set version in package.json"
inputs:
targetType: "inline"
script: |
$pkg = Get-Content -Path "$(System.DefaultWorkingDirectory)/functions/package.json" -Raw | ConvertFrom-Json
$pkg.version = "${{ parameters.buildNumber }}"
$pkg | ConvertTo-Json -Depth 100 | Set-Content -Path "$(System.DefaultWorkingDirectory)/functions/package.json"
- task: CmdLine@2
displayName: "Copy .env.example to .env"
inputs:
script: |
cp "$(System.DefaultWorkingDirectory)/functions/.env.example" "$(System.DefaultWorkingDirectory)/functions/.env"
- task: ReplaceTokens@3
displayName: "Replace tokens in .env file"
inputs:
targetFiles: "$(System.DefaultWorkingDirectory)/functions/.env"
tokenPrefix: "#{"
tokenSuffix: "}#"
- task: Npm@1
displayName: "npm run build"
inputs:
command: "custom"
workingDir: "$(System.DefaultWorkingDirectory)/functions"
customCommand: "run build"
- task: DeleteFiles@1
displayName: "Remove node_modules, *.log files, src directory from functions directory"
inputs:
SourceFolder: "$(System.DefaultWorkingDirectory)/functions"
Contents: |
node_modules/**
*.log
src/**
- task: ArchiveFiles@2
displayName: "Archive functions directory"
inputs:
rootFolderOrFile: "$(System.DefaultWorkingDirectory)/functions"
includeRootFolder: false
archiveFile: "$(System.DefaultWorkingDirectory)/fitlien-services-$(buildNumber).zip"
compression: "zip"
- task: CopyFiles@2
displayName: "Copy archive to staging directory"
inputs:
SourceFolder: "$(System.DefaultWorkingDirectory)"
Contents: "fitlien-services-$(buildNumber).zip"
TargetFolder: "$(System.ArtifactsDirectory)"

6
functions/.env.example Normal file
View File

@ -0,0 +1,6 @@
MAILGUN_API_KEY=#{MAILGUN_API_KEY}#
MAILGUN_SERVER=#{MAILGUN_SERVER}#
MAILGUN_FROM_ADDRESS=#{MAILGUN_FROM_ADDRESS}#
TWILIO_ACCOUNT_SID=AC5cfaae728ba68fb1aa6756d973b6e32b
TWILIO_AUTH_TOKEN=886ed704c7918078f361f5f88b42ffc0
TWILIO_PHONE_NUMBER=+12315005309

View File

@ -1,15 +1,18 @@
{
"name": "functions",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "functions",
"version": "0.0.0",
"dependencies": {
"firebase-admin": "^12.6.0",
"firebase-functions": "^6.0.1",
"form-data": "^4.0.1",
"html-to-text": "^9.0.5",
"long": "4.0.0",
"mailgun.js": "^10.4.0",
"twilio": "^5.4.0"
},
@ -765,6 +768,12 @@
"node": ">=6"
}
},
"node_modules/@grpc/proto-loader/node_modules/long": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
"integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==",
"optional": true
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@ -2743,9 +2752,9 @@
}
},
"node_modules/firebase-functions": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.2.0.tgz",
"integrity": "sha512-vfyyVHS8elxplzEQ9To+NaINRPFUsDasQrasTa2eFJBYSPzdhkw6rwLmvwyYw622+ze+g4sDIb14VZym+afqXQ==",
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.2.tgz",
"integrity": "sha512-FC3A1/nhqt1ZzxRnj5HZLScQaozAcFSD/vSR8khqSoFNOfxuXgwJS6ZABTB7+v+iMD5z6Mmxw6OfqITUBuI7OQ==",
"dependencies": {
"@types/cors": "^2.8.5",
"@types/express": "^4.17.21",
@ -4425,9 +4434,9 @@
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
},
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"node_modules/lru-cache": {
"version": "5.1.1",
@ -5035,6 +5044,11 @@
"node": ">=12.0.0"
}
},
"node_modules/protobufjs/node_modules/long": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
"integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",

View File

@ -1,5 +1,6 @@
{
"name": "functions",
"version": "0.0.0",
"scripts": {
"build": "tsc",
"build:watch": "tsc --watch",
@ -18,6 +19,7 @@
"firebase-functions": "^6.0.1",
"form-data": "^4.0.1",
"html-to-text": "^9.0.5",
"long": "4.0.0",
"mailgun.js": "^10.4.0",
"twilio": "^5.4.0"
},

View File

@ -1,11 +1,16 @@
import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https";
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import * as admin from 'firebase-admin';
import * as express from "express";
import * as logger from "firebase-functions/logger";
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const { convert } = require('html-to-text');
const twilio = require('twilio')
export const sendEmail = onRequest((request, response) => {
export const sendEmail = onRequest((request: Request, response: express.Response) => {
const mailgun = new Mailgun(formData);
const mailGunClient = mailgun.client({ username: 'api', key: process.env.MAILGUN_API_KEY });
@ -32,11 +37,9 @@ export const sendEmail = onRequest((request, response) => {
});
});
export const sendSMS = onRequest((request, response) => {
export const sendSMS = onRequest((request: Request, response: express.Response) => {
const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const { to, body } = request.body;
client.messages
.create({
body: body,
@ -52,3 +55,81 @@ export const sendSMS = onRequest((request, response) => {
response.status(500).json({ success: false, error: error.message });
});
});
interface Invitation {
email: string;
phoneNumber: string;
gymName: string;
invitedByName: string;
}
export const sendInvitationNotification = onDocumentCreated(
'invitations/{invitationId}',
async (event) => {
const invitation = event.data?.data() as Invitation;
const invitationId = event.params.invitationId;
if (!invitation) {
console.error('Invitation data is missing.');
return null;
}
try {
const userQuery = await admin
.firestore()
.collection('users')
.where('email', '==', invitation.email)
.where('phoneNumber', '==', invitation.phoneNumber)
.limit(1)
.get();
if (userQuery.empty) {
console.log(
`User not found for email: ${invitation.email} and phone: ${invitation.phoneNumber}.`
);
return null;
}
const user = userQuery.docs[0].data();
const fcmToken = user.fcmToken;
if (!fcmToken) {
console.log(`FCM token not found for user: ${invitation.email}.`);
return null;
}
const message: admin.messaging.Message = {
notification: {
title: 'New Gym Invitation',
body: `${invitation.invitedByName} has invited you to join ${invitation.gymName}`,
},
data: {
type: 'invitation',
invitationId: invitationId,
gymName: invitation.gymName,
senderName: invitation.invitedByName,
},
android: {
priority: 'high',
notification: {
channelId: 'invitations_channel',
priority: 'high',
defaultSound: true,
defaultVibrateTimings: true,
icon: '@mipmap/ic_launcher',
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
},
},
token: fcmToken,
};
await admin.messaging().send(message);
console.log(`Invitation notification sent to ${invitation.email}.`);
return null;
} catch (error) {
console.error('Error sending invitation notification:', error);
return null;
}
}
);

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"module": "NodeNext",
"esModuleInterop": true,
"moduleResolution": "nodenext",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node16",
"noImplicitReturns": true,
"noUnusedLocals": true,
"outDir": "lib",