Skip to content

Commit e287eed

Browse files
author
Andrei Damian
committed
added workflow + test function
1 parent c1d141b commit e287eed

2 files changed

Lines changed: 136 additions & 0 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Deploy Firebase Functions on push
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
target-bucket:
7+
type: string
8+
required: true
9+
function-name:
10+
type: string
11+
required: true
12+
project:
13+
type: string
14+
required: true
15+
16+
17+
jobs:
18+
deploy:
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
- name: Checkout repo
23+
uses: actions/checkout@v3
24+
25+
- name: Set up Node.js
26+
uses: actions/setup-node@v3
27+
with:
28+
node-version: '22'
29+
30+
- name: Create .env file
31+
run: |
32+
cd functions
33+
echo "TARGET_BUCKET=${{ inputs.target-bucket }}" > .env
34+
35+
- name: Install Firebase CLI
36+
run: npm install -g firebase-tools
37+
38+
- name: Install dependencies
39+
run: |
40+
cd functions
41+
npm install
42+
43+
- name: Deploy Firebase Functions
44+
run: |
45+
echo '${{ secrets.GCP_SA_KEY }}' > $HOME/gcp-key.json
46+
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/gcp-key.json"
47+
firebase deploy --only functions:${{ inputs.function-name }} --project=${{ inputs.project }}

functions/src/index.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,96 @@ if (!process.env.TARGET_BUCKET) {
1616

1717
admin.initializeApp();
1818
console.log("Firebase admin initialized.");
19+
export const damaTest=onCall(
20+
{
21+
enforceAppCheck: true,
22+
secrets: [],
23+
},
24+
async (request) => {
25+
const data = request.data;
26+
27+
if (!data || typeof data !== "object" || Object.keys(data).length === 0) {
28+
throw new HttpsError(
29+
"invalid-argument",
30+
"Data must be a non-empty JSON object."
31+
);
32+
}
33+
34+
const {folderPrefix, userPseudoID, payload} = data;
35+
const appCheckToken = request.rawRequest.headers["x-firebase-appcheck"] as string;
36+
37+
const isEmulator = process.env.FUNCTIONS_EMULATOR === "true";
38+
let firebaseAppId = "emulator_app_id";
39+
40+
if (isEmulator) {
41+
logger.debug("Running in emulator, skipping App Check verification.");
42+
firebaseAppId = "emulator_app_id";
43+
} else {
44+
if (!appCheckToken || typeof appCheckToken !== "string") {
45+
throw new HttpsError("unauthenticated", "App Check token is missing or malformed.");
46+
}
47+
48+
try {
49+
const decodedAppCheckToken = await admin.appCheck().verifyToken(appCheckToken);
50+
firebaseAppId = decodedAppCheckToken.appId;
51+
} catch (err) {
52+
logger.error("App Check token verification failed:", err);
53+
throw new HttpsError("unauthenticated", "Invalid App Check token.");
54+
}
55+
}
56+
57+
if (!payload || typeof payload !== "object" || Object.keys(payload).length === 0) {
58+
throw new HttpsError(
59+
"invalid-argument",
60+
"Payload must be a non-empty JSON object."
61+
);
62+
}
63+
64+
if (!folderPrefix || !userPseudoID) {
65+
throw new HttpsError(
66+
"invalid-argument",
67+
"Missing folderPrefix or userPseudoID in the payload."
68+
);
69+
}
70+
71+
const projectId = process.env.GCP_PROJECT || process.env.GCLOUD_PROJECT;
72+
73+
if (!projectId) {
74+
logger.error("Project ID is not set. Ensure GCP_PROJECT or GCLOUD_PROJECT is defined in the environment.");
75+
throw new HttpsError("internal", "Missing project ID configuration.");
76+
}
77+
78+
const appInfo = await mapAppIdToAppDetails(projectId!, firebaseAppId);
79+
const platformId = appInfo.platformId;
80+
81+
const filePath = generateFilePath(userPseudoID, folderPrefix, platformId);
82+
83+
logger.log("Saving Payload data to:", filePath);
84+
85+
const bucketName = process.env.TARGET_BUCKET;
1986

87+
console.log("TARGET_BUCKET:", bucketName);
88+
89+
if (!bucketName) {
90+
throw new HttpsError(
91+
"internal",
92+
"TARGET_BUCKET environment variable is not set."
93+
);
94+
}
95+
96+
const bucket = admin.storage().bucket(bucketName);
97+
98+
await checkBucketWritePermission(bucket);
99+
100+
const jsonLines = JSON.stringify(JSON.parse(JSON.stringify(payload, Object.keys(payload).sort())));
101+
102+
await bucket.file(filePath).save(jsonLines, {
103+
contentType: "application/json",
104+
});
105+
106+
return {success: true, filePath: `gs://${bucketName}/${filePath}`};
107+
}
108+
);
20109
export const savePayload=onCall(
21110
{
22111
enforceAppCheck: true,

0 commit comments

Comments
 (0)