diff --git a/common/access.ts b/common/access.ts new file mode 100644 index 00000000..acd894b1 --- /dev/null +++ b/common/access.ts @@ -0,0 +1,14 @@ +export function isWhitelisted(email?: string) { + return true + // e.g. return email.endsWith('@theoremone.co') || isAdmin(email) +} + +export function isAdmin(email: string) { + const ADMINS = [ + 'akrolsmir@gmail.com', // Austin + 'jahooma@gmail.com', // James + 'taowell@gmail.com', // Stephen + 'manticmarkets@gmail.com', // Manifold + ] + return ADMINS.includes(email) +} diff --git a/firebase.json b/firebase.json index 988e04a4..8da7d8f3 100644 --- a/firebase.json +++ b/firebase.json @@ -5,6 +5,7 @@ "source": "functions" }, "firestore": { - "rules": "firestore.rules" + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" } } diff --git a/firestore.indexes.json b/firestore.indexes.json new file mode 100644 index 00000000..0576f860 --- /dev/null +++ b/firestore.indexes.json @@ -0,0 +1,234 @@ +{ + "indexes": [ + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "creatorId", + "order": "ASCENDING" + }, + { + "fieldPath": "createdTime", + "order": "DESCENDING" + } + ] + }, + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "isResolved", + "order": "ASCENDING" + }, + { + "fieldPath": "closeTime", + "order": "ASCENDING" + } + ] + }, + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "isResolved", + "order": "ASCENDING" + }, + { + "fieldPath": "visibility", + "order": "ASCENDING" + }, + { + "fieldPath": "closeTime", + "order": "ASCENDING" + } + ] + }, + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "isResolved", + "order": "ASCENDING" + }, + { + "fieldPath": "visibility", + "order": "ASCENDING" + }, + { + "fieldPath": "volume24Hours", + "order": "DESCENDING" + } + ] + }, + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "isResolved", + "order": "ASCENDING" + }, + { + "fieldPath": "volume24Hours", + "order": "DESCENDING" + } + ] + }, + { + "collectionGroup": "contracts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "slug", + "order": "ASCENDING" + }, + { + "fieldPath": "createdTime", + "order": "DESCENDING" + } + ] + } + ], + "fieldOverrides": [ + { + "collectionGroup": "answers", + "fieldPath": "username", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "bets", + "fieldPath": "createdTime", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "bets", + "fieldPath": "userId", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "comments", + "fieldPath": "createdTime", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "comments", + "fieldPath": "userUsername", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "followers", + "fieldPath": "userId", + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + } + ] +} diff --git a/functions/.gitignore b/functions/.gitignore index e0ba0181..8b54e3dc 100644 --- a/functions/.gitignore +++ b/functions/.gitignore @@ -1,3 +1,6 @@ +# Secrets +.env* + # Compiled JavaScript files lib/**/*.js lib/**/*.js.map diff --git a/functions/src/create-user.ts b/functions/src/create-user.ts index 13c880f3..f583abe4 100644 --- a/functions/src/create-user.ts +++ b/functions/src/create-user.ts @@ -14,6 +14,7 @@ import { cleanUsername, } from '../../common/util/clean-username' import { sendWelcomeEmail } from './emails' +import { isWhitelisted } from '../../common/access' export const createUser = functions .runWith({ minInstances: 1 }) @@ -32,6 +33,9 @@ export const createUser = functions const fbUser = await admin.auth().getUser(userId) const email = fbUser.email + if (!isWhitelisted(email)) { + return { status: 'error', message: `${email} is not whitelisted` } + } const emailName = email?.replace(/@.*$/, '') const rawName = fbUser.displayName || emailName || 'User' + randomString(4) diff --git a/web/hooks/use-admin.ts b/web/hooks/use-admin.ts index 733f73f6..bbeaf59c 100644 --- a/web/hooks/use-admin.ts +++ b/web/hooks/use-admin.ts @@ -1,12 +1,8 @@ -import { useUser } from './use-user' +import { isAdmin } from '../../common/access' +import { usePrivateUser, useUser } from './use-user' export const useAdmin = () => { const user = useUser() - const adminIds = [ - 'igi2zGXsfxYPgB0DJTXVJVmwCOr2', // Austin - '5LZ4LgYuySdL1huCWe7bti02ghx2', // James - 'tlmGNz9kjXc2EteizMORes4qvWl2', // Stephen - 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2', // Manifold - ] - return adminIds.includes(user?.id || '') + const privateUser = usePrivateUser(user?.id) + return isAdmin(privateUser?.email || '') } diff --git a/web/lib/firebase/init.ts b/web/lib/firebase/init.ts index 64e808de..145d67c5 100644 --- a/web/lib/firebase/init.ts +++ b/web/lib/firebase/init.ts @@ -1,28 +1,41 @@ import { getFirestore } from '@firebase/firestore' import { initializeApp, getApps, getApp } from 'firebase/app' +// Used to decide which Stripe instance to point to export const isProd = process.env.NEXT_PUBLIC_FIREBASE_ENV !== 'DEV' -const firebaseConfig = isProd - ? { - apiKey: 'AIzaSyDp3J57vLeAZCzxLD-vcPaGIkAmBoGOSYw', - authDomain: 'mantic-markets.firebaseapp.com', - projectId: 'mantic-markets', - storageBucket: 'mantic-markets.appspot.com', - messagingSenderId: '128925704902', - appId: '1:128925704902:web:f61f86944d8ffa2a642dc7', - measurementId: 'G-SSFK1Q138D', - } - : { - apiKey: 'AIzaSyBoq3rzUa8Ekyo3ZaTnlycQYPRCA26VpOw', - authDomain: 'dev-mantic-markets.firebaseapp.com', - projectId: 'dev-mantic-markets', - storageBucket: 'dev-mantic-markets.appspot.com', - messagingSenderId: '134303100058', - appId: '1:134303100058:web:27f9ea8b83347251f80323', - measurementId: 'G-YJC9E37P37', - } - +const FIREBASE_CONFIGS = { + PROD: { + apiKey: 'AIzaSyDp3J57vLeAZCzxLD-vcPaGIkAmBoGOSYw', + authDomain: 'mantic-markets.firebaseapp.com', + projectId: 'mantic-markets', + storageBucket: 'mantic-markets.appspot.com', + messagingSenderId: '128925704902', + appId: '1:128925704902:web:f61f86944d8ffa2a642dc7', + measurementId: 'G-SSFK1Q138D', + }, + DEV: { + apiKey: 'AIzaSyBoq3rzUa8Ekyo3ZaTnlycQYPRCA26VpOw', + authDomain: 'dev-mantic-markets.firebaseapp.com', + projectId: 'dev-mantic-markets', + storageBucket: 'dev-mantic-markets.appspot.com', + messagingSenderId: '134303100058', + appId: '1:134303100058:web:27f9ea8b83347251f80323', + measurementId: 'G-YJC9E37P37', + }, + THEOREMONE: { + apiKey: 'AIzaSyBSXL6Ys7InNHnCKSy-_E_luhh4Fkj4Z6M', + authDomain: 'theoremone-manifold.firebaseapp.com', + projectId: 'theoremone-manifold', + storageBucket: 'theoremone-manifold.appspot.com', + messagingSenderId: '698012149198', + appId: '1:698012149198:web:b342af75662831aa84b79f', + measurementId: 'G-Y3EZ1WNT6E', + }, +} +const ENV = process.env.NEXT_PUBLIC_FIREBASE_ENV ?? 'PROD' +// @ts-ignore +const firebaseConfig = FIREBASE_CONFIGS[ENV] // Initialize Firebase export const app = getApps().length ? getApp() : initializeApp(firebaseConfig)