Skip to content

Commit

Permalink
Install Firebase storage rules, emulator & new functions API
Browse files Browse the repository at this point in the history
  • Loading branch information
ekzhang committed Dec 22, 2024
1 parent 0ac422b commit 18ebf3f
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ jobs:
- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy -P staging --only database,functions,hosting
args: deploy -P staging --only database,functions,hosting,storage
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy -P default --only database,functions
args: deploy -P default --only database,functions,storage
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

Expand Down
18 changes: 12 additions & 6 deletions firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"database": {
"rules": "database.rules.json"
},
"functions": {
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"],
"source": "functions"
},
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
Expand All @@ -12,26 +16,28 @@
}
]
},
"functions": {
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run build"],
"source": "functions"
"storage": {
"rules": "storage.rules"
},
"emulators": {
"auth": {
"port": 9099
},
"functions": {
"port": 5001
},
"database": {
"port": 9000
},
"functions": {
"port": 5001
},
"hosting": {
"port": 9001
},
"pubsub": {
"port": 8085
},
"storage": {
"port": 9199
},
"ui": {
"enabled": true,
"port": 4000
Expand Down
53 changes: 22 additions & 31 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import * as admin from "firebase-admin";
import { initializeApp } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
import { type DataSnapshot, getDatabase } from "firebase-admin/database";
import * as functions from "firebase-functions/v1";
import Stripe from "stripe";

import { GameMode, findSet, generateDeck, replayEvents } from "./game";

admin.initializeApp();
initializeApp(); // Sets the default Firebase app.

const stripe = process.env.FUNCTIONS_EMULATOR
? (null as any)
Expand All @@ -21,7 +23,7 @@ const BASE_RATING = 1200;

type TransactionResult = {
committed: boolean;
snapshot: admin.database.DataSnapshot;
snapshot: DataSnapshot;
};

/** Ends the game with the correct time and updates ratings */
Expand All @@ -45,11 +47,8 @@ export const finishGame = functions.https.onCall(async (data, context) => {
);
}

const gameData = await admin
.database()
.ref(`gameData/${gameId}`)
.once("value");
const gameSnap = await admin.database().ref(`games/${gameId}`).once("value");
const gameData = await getDatabase().ref(`gameData/${gameId}`).once("value");
const gameSnap = await getDatabase().ref(`games/${gameId}`).once("value");
if (!gameSnap.exists()) {
throw new functions.https.HttpsError(
"not-found",
Expand All @@ -70,8 +69,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {

// The game has ended, so we attempt to do an atomic update.
// Safety: Events can only be appended to the game, so the final time must remain the same.
const { committed, snapshot }: TransactionResult = await admin
.database()
const { committed, snapshot }: TransactionResult = await getDatabase()
.ref(`games/${gameId}`)
.transaction((game) => {
if (game.status !== "ingame") {
Expand Down Expand Up @@ -124,8 +122,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {
const userStats: Record<string, any> = {};
await Promise.all(
players.map(async (player) => {
const result: TransactionResult = await admin
.database()
const result: TransactionResult = await getDatabase()
.ref(`userStats/${player}/${gameMode}/${variant}`)
.transaction((stats) => {
stats ??= {}; // tslint:disable-line no-parameter-reassignment
Expand All @@ -151,8 +148,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {
// Retrieve old ratings from the database.
const ratings: Record<string, number> = {};
for (const player of players) {
const ratingSnap = await admin
.database()
const ratingSnap = await getDatabase()
.ref(`userStats/${player}/${gameMode}/rating`)
.once("value");
const rating = ratingSnap.exists() ? ratingSnap.val() : BASE_RATING;
Expand Down Expand Up @@ -192,7 +188,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {

updates[`${player}/${gameMode}/rating`] = newRating;
}
await admin.database().ref("userStats").update(updates);
await getDatabase().ref("userStats").update(updates);
});

/** Create a new game in the database */
Expand Down Expand Up @@ -233,16 +229,15 @@ export const createGame = functions.https.onCall(async (data, context) => {
const userId = context.auth.uid;

const oneHourAgo = Date.now() - 3600000;
const recentGameIds = await admin
.database()
const recentGameIds = await getDatabase()
.ref(`userGames/${userId}`)
.orderByValue()
.startAt(oneHourAgo)
.once("value");

const recentGames = await Promise.all(
Object.keys(recentGameIds.val() || {}).map((recentGameId) =>
admin.database().ref(`games/${recentGameId}`).once("value"),
getDatabase().ref(`games/${recentGameId}`).once("value"),
),
);

Expand All @@ -257,7 +252,7 @@ export const createGame = functions.https.onCall(async (data, context) => {
}
}

const gameRef = admin.database().ref(`games/${gameId}`);
const gameRef = getDatabase().ref(`games/${gameId}`);
const { committed, snapshot } = await gameRef.transaction((currentData) => {
if (currentData === null) {
if (
Expand Down Expand Up @@ -297,20 +292,18 @@ export const createGame = functions.https.onCall(async (data, context) => {
// 3. /publicGames (if access is public)
const updates: Array<Promise<any>> = [];
updates.push(
admin.database().ref(`gameData/${gameId}`).set({
getDatabase().ref(`gameData/${gameId}`).set({
deck: generateDeck(),
}),
);
updates.push(
admin
.database()
getDatabase()
.ref("stats/gameCount")
.transaction((count) => (count || 0) + 1),
);
if (access === "public") {
updates.push(
admin
.database()
getDatabase()
.ref("publicGames")
.child(gameId)
.set(snapshot?.child("createdAt").val()),
Expand All @@ -330,7 +323,7 @@ export const customerPortal = functions.https.onCall(async (data, context) => {
);
}

const user = await admin.auth().getUser(context.auth.uid);
const user = await getAuth().getUser(context.auth.uid);
if (!user.email) {
throw new functions.https.HttpsError(
"failed-precondition",
Expand All @@ -357,8 +350,7 @@ export const customerPortal = functions.https.onCall(async (data, context) => {
export const clearConnections = functions.pubsub
.schedule("every 1 minutes")
.onRun(async (context) => {
const onlineUsers = await admin
.database()
const onlineUsers = await getDatabase()
.ref("users")
.orderByChild("connections")
.startAt(false)
Expand All @@ -372,7 +364,7 @@ export const clearConnections = functions.pubsub
actions.push(snap.ref.child("connections").remove());
}
});
actions.push(admin.database().ref("stats/onlineUsers").set(numUsers));
actions.push(getDatabase().ref("stats/onlineUsers").set(numUsers));
await Promise.all(actions);
});

Expand Down Expand Up @@ -406,14 +398,13 @@ export const handleStripe = functions.https.onRequest(async (req, res) => {
)) as Stripe.Response<Stripe.Customer>;

if (email) {
const user = await admin
.auth()
const user = await getAuth()
.getUserByEmail(email)
.catch(() => null);

if (user) {
const newState = event.type === "customer.subscription.created";
await admin.database().ref(`users/${user.uid}/patron`).set(newState);
await getDatabase().ref(`users/${user.uid}/patron`).set(newState);
console.log(`Processed ${email} (${user.uid}): newState = ${newState}`);
} else {
console.log(`Failed to find user: ${email}`);
Expand Down
1 change: 1 addition & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const config = {
authDomain: "setwithfriends-dev.web.app",
databaseURL: "https://setwithfriends-dev.firebaseio.com",
projectId: "setwithfriends-dev",
storageBucket: "setwithfriends-dev.appspot.com",
appId: "1:369319422608:web:b9038b38a1bd598048c615",
},
stripe: null, // Stripe not supported in development
Expand Down
3 changes: 3 additions & 0 deletions src/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import "firebase/compat/auth";
import "firebase/compat/database";
import "firebase/compat/functions";

// import "firebase/compat/storage";

import config, { isDev } from "./config";

firebase.initializeApp(config.firebase);
Expand All @@ -13,6 +15,7 @@ if (isDev) {
.useEmulator("http://localhost:9099", { disableWarnings: true });
firebase.database().useEmulator("localhost", 9000);
firebase.functions().useEmulator("localhost", 5001);
// firebase.storage().useEmulator("localhost", 9199);
} else {
firebase.analytics();
}
Expand Down
11 changes: 11 additions & 0 deletions storage.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Security rules for accessing GCS for Firebase.
// https://firebase.google.com/docs/storage/security/core-syntax

rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// Deny access to the storage bucket.
// Currently, we only access GCS from cloud functions.
allow read, write: if false;
}
}

0 comments on commit 18ebf3f

Please sign in to comment.