Skip to content

Commit

Permalink
Merge pull request #2631 from build-5/api_project
Browse files Browse the repository at this point in the history
Require project api key
  • Loading branch information
adamunchained authored Nov 6, 2023
2 parents 4d6ac6b + 66e683d commit e50dc7d
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 57 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/functions_emulated-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,8 @@ jobs:
npm run test:ci -- --forceExit --findRelatedTests ./test/cron/proposal.cron.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/db.roll.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/dbroll/nft.auction.roll.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/stake/delete.stake.reward.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/stake/stake.reward.cron.spec.ts
npm run test:ci -- --forceExit --findRelatedTests ./test/dbroll/set.project.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/stake/delete.stake.reward.spec.ts
" --project dev --only functions,firestore,storage,ui,auth --export-on-exit=./firestore-data
- name: Archive firestore data
uses: actions/upload-artifact@v3
Expand Down Expand Up @@ -298,6 +298,7 @@ jobs:
export GOOGLE_APPLICATION_CREDENTIALS="./test-service-account-key.json"
npm run milestone-sync &
firebase emulators:exec "
npm run test:ci -- --forceExit --findRelatedTests ./test/stake/stake.reward.cron.spec.ts &&
npm run test:ci -- --forceExit --findRelatedTests ./test/storage/resize.img.spec.ts
" --project dev --only functions,firestore,storage,ui,auth --export-on-exit=./firestore-data
- name: Archive firestore data
Expand Down
39 changes: 37 additions & 2 deletions .github/workflows/functions_online-emulated-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,9 @@ jobs:
npm run test-online:ci -- --forceExit --findRelatedTests ./test/cron/proposal.cron.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/db.roll.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/dbroll/nft.auction.roll.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/dbroll/set.project.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/stake/delete.stake.reward.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/stake/stake.reward.cron.spec.ts &&
npm run test-online:ci -- --forceExit --findRelatedTests ./test/storage/resize.img.spec.ts
npm run test-online:ci -- --forceExit --findRelatedTests ./test/stake/stake.reward.cron.spec.ts
" --project dev --only functions,firestore,storage,ui,auth --export-on-exit=./firestore-data
- name: Archive firestore data
uses: actions/upload-artifact@v3
Expand All @@ -274,3 +274,38 @@ jobs:
name: firestore-data-test-online-chunk_5
path: ./packages/functions/firestore-data/
retention-days: 1
chunk_6:
needs: npm-install
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16.x
- uses: actions/cache@v3
with:
path: |
node_modules
packages/functions/node_modules
packages/interfaces/node_modules
key: ${{ runner.os }}-modules-${{ hashFiles('**/package.json') }}
- name: Init
run: |
npm run build:functions
npm install -g firebase-tools
- name: Test
working-directory: packages/functions
run: |
export GOOGLE_APPLICATION_CREDENTIALS="./test-service-account-key.json"
npm run milestone-sync &
firebase emulators:exec "
npm run test-online:ci -- --forceExit --findRelatedTests ./test/storage/resize.img.spec.ts
" --project dev --only functions,firestore,storage,ui,auth --export-on-exit=./firestore-data
- name: Archive firestore data
uses: actions/upload-artifact@v3
if: ${{ failure() }}
with:
name: firestore-data-test-online-chunk_6
path: ./packages/functions/firestore-data/
retention-days: 1
4 changes: 3 additions & 1 deletion packages/api/src/getMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const getManySchema = Joi.object({
startAfter: CommonJoi.uid(false),
});

export const getMany = async (url: string) => {
export const getMany = async (project: string, url: string) => {
const body = getQueryParams<GetManyRequest>(url, getManySchema);

const baseCollectionPath =
Expand All @@ -52,6 +52,8 @@ export const getMany = async (url: string) => {
.collection(baseCollectionPath as COL)
.limit(getQueryLimit(body.collection));

query = query.where(`projects.${project}`, '==', true);

if (body.fieldName && body.fieldValue != null) {
try {
const filters = getFilters(body.fieldName, body.fieldValue);
Expand Down
4 changes: 3 additions & 1 deletion packages/api/src/getManyAdvanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ const getManyAdvancedSchema = Joi.object({
startAfter: CommonJoi.uid(false),
});

export const getManyAdvanced = async (url: string) => {
export const getManyAdvanced = async (project: string, url: string) => {
const body = getQueryParams<GetManyAdvancedRequest>(url, getManyAdvancedSchema);

const { collection, subCollection, uid } = body;
let query = getBaseQuery(collection, uid, subCollection).limit(getQueryLimit(body.collection));

query = query.where(`projects.${project}`, '==', true);

const { filters, operators } = getFilters(body.fieldName, body.fieldValue, body.operator);
try {
for (const [key, values] of Object.entries(filters)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/api/src/getUpdatedAfter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const getUpdatedAfterSchema = Joi.object({
startAfter: CommonJoi.uid(false),
});

export const getUpdatedAfter = async (url: string) => {
export const getUpdatedAfter = async (project: string, url: string) => {
const body = getQueryParams<GetUpdatedAfterRequest>(url, getUpdatedAfterSchema);

const isSubCollectionQuery = body.subCollection && body.uid;
Expand All @@ -38,6 +38,7 @@ export const getUpdatedAfter = async (url: string) => {
.collection(baseCollectionPath as COL)
.where('updatedOn', '>=', updatedAfter.toDate())
.orderBy('updatedOn')
.where(`projects.${project}`, '==', true)
.limit(getQueryLimit(body.collection));

if (body.collection === PublicCollections.NFT) {
Expand Down
37 changes: 29 additions & 8 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ApiRoutes } from '@build-5/interfaces';
import cors from 'cors';
import express from 'express';
import http from 'http';
import jwt from 'jsonwebtoken';
import { get } from 'lodash';
import { Observable, first } from 'rxjs';
import ws from 'ws';
import { getAddresses } from './getAddresses';
Expand All @@ -23,12 +26,12 @@ const app = express();

app.use(cors());

app.get('/*', (req, res) => onConnection(req.url, res));
app.get('/*', (req, res) => onConnection(req, res));

const wsServer = new ws.Server({ noServer: true });

wsServer.on('connection', async (socket, request) => {
onConnection(request.url || '', socket);
onConnection(request, socket);
});

const server = app.listen(port);
Expand All @@ -41,9 +44,13 @@ server.on('upgrade', (request, socket, head) => {
});
});

const onConnection = async (url: string, res: express.Response | ws.WebSocket) => {
const onConnection = async (
req: express.Request | http.IncomingMessage,
res: express.Response | ws.WebSocket,
) => {
try {
const observable = await getObservable(url);
const project = getProjectId(req);
const observable = await getObservable(project, req.url || '');
if (res instanceof ws.WebSocket) {
sendLiveUpdates(res, observable);
return;
Expand All @@ -61,19 +68,19 @@ const onConnection = async (url: string, res: express.Response | ws.WebSocket) =
}
};

const getObservable = (url: string): Promise<Observable<unknown>> => {
const getObservable = (project: string, url: string): Promise<Observable<unknown>> => {
const route = url.replace('/api', '').split('?')[0];
switch (route) {
case ApiRoutes.GET_BY_ID:
return getById(url);
case ApiRoutes.GET_MANY_BY_ID:
return getManyById(url);
case ApiRoutes.GET_MANY:
return getMany(url);
return getMany(project, url);
case ApiRoutes.GET_MANY_ADVANCED:
return getManyAdvanced(url);
return getManyAdvanced(project, url);
case ApiRoutes.GET_UPDATED_AFTER:
return getUpdatedAfter(url);
return getUpdatedAfter(project, url);
case ApiRoutes.GET_TOKEN_PRICE:
return getTokenPrice(url);
case ApiRoutes.GET_AVG_PRICE:
Expand All @@ -94,3 +101,17 @@ const getObservable = (url: string): Promise<Observable<unknown>> => {
throw { code: 400, message: 'Invalid route' };
}
};

const getProjectId = (req: express.Request | http.IncomingMessage) => {
try {
const jwtToken = req.headers.authorization?.split(' ')[1];
const payload = jwt.verify(jwtToken || '', 'asdas#@#@xdas31sad');
const project = get(payload, 'project', '');
if (!project) {
throw { code: 401, message: 'Unauthorized' };
}
return project;
} catch {
throw { code: 401, message: 'Unauthorized' };
}
};
19 changes: 9 additions & 10 deletions packages/functions/scripts/dbUpgrades/1.0.0/auction.roll.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FirebaseApp, Firestore, build5Db } from '@build-5/database';
import { FirebaseApp, Firestore } from '@build-5/database';
import {
Auction,
AuctionType,
Expand All @@ -12,7 +12,7 @@ import {
import { randomBytes } from 'crypto';
import dayjs from 'dayjs';
import { Wallet } from 'ethers';
import { get, head } from 'lodash';
import { get, head, last } from 'lodash';

export const nftAuctionRoll = async (app: FirebaseApp) => {
const db = new Firestore(app);
Expand All @@ -28,18 +28,19 @@ export const nftAuctionRoll = async (app: FirebaseApp) => {
.startAfter(lastDoc)
.limit(500)
.get<Nft>();
lastDocId = last(nfts)?.uid || '';

const promises = nfts.map((n) =>
build5Db().runTransaction(async (transaction) => {
const nftDocRef = build5Db().doc(`${COL.NFT}/${n.uid}`);
db.runTransaction(async (transaction) => {
const nftDocRef = db.doc(`${COL.NFT}/${n.uid}`);
const nft = <Nft>await transaction.get(nftDocRef);

if (nft.auction || !nft.auctionTo || dayjs(nft.auctionTo.toDate()).isBefore(dayjs())) {
return;
}

const auction = await getAuctionData(nft);
const auctionDocRef = build5Db().doc(`${COL.AUCTION}/${auction.uid}`);
const auction = await getAuctionData(db, nft);
const auctionDocRef = db.doc(`${COL.AUCTION}/${auction.uid}`);
transaction.create(auctionDocRef, auction);

transaction.update(nftDocRef, { auction: auction.uid });
Expand All @@ -50,7 +51,7 @@ export const nftAuctionRoll = async (app: FirebaseApp) => {
} while (lastDocId);
};

const getAuctionData = async (nft: Nft) => {
const getAuctionData = async (db: Firestore, nft: Nft) => {
const auction: Auction = {
uid: getRandomEthAddress(),
space: nft.space,
Expand All @@ -77,9 +78,7 @@ const getAuctionData = async (nft: Nft) => {
auction.auctionHighestBidder = nft.auctionHighestBidder;
auction.auctionHighestBid = nft.auctionHighestBid || 0;

const paymentDocRef = build5Db().doc(
`${COL.TRANSACTION}/${get(nft, 'auctionHighestTransaction', '')}`,
);
const paymentDocRef = db.doc(`${COL.TRANSACTION}/${get(nft, 'auctionHighestTransaction', '')}`);
const payment = <Transaction>await paymentDocRef.get();
auction.bids.push({
bidder: nft.auctionHighestBidder,
Expand Down
93 changes: 93 additions & 0 deletions packages/functions/scripts/dbUpgrades/1.0.0/set.project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { FirebaseApp } from '@build-5/database';
import { BaseRecord, COL, SOON_PROJECT_ID, SUB_COL } from '@build-5/interfaces';
import admin from 'firebase-admin';
import { last } from 'lodash';

const collections = [
COL.AWARD,
COL.COLLECTION,
COL.NFT,
COL.SPACE,
COL.PROPOSAL,
COL.TRANSACTION,
COL.BADGES,
COL.TOKEN,
COL.TOKEN_MARKET,
COL.TOKEN_PURCHASE,
COL.STAKE,
COL.STAKE_REWARD,
COL.NFT_STAKE,
COL.AIRDROP,
];

const subCollections = [
SUB_COL.OWNERS,
SUB_COL.PARTICIPANTS,
SUB_COL.MEMBERS,
SUB_COL.GUARDIANS,
SUB_COL.ADMINS,
SUB_COL.BLOCKED_MEMBERS,
SUB_COL.KNOCKING_MEMBERS,
SUB_COL.DISTRIBUTION,
SUB_COL.STATS,
SUB_COL.VOTES,
SUB_COL.RANKS,
];

type DocType = admin.firestore.QueryDocumentSnapshot<admin.firestore.DocumentData> | undefined;
type QuerySnap = admin.firestore.QuerySnapshot<admin.firestore.DocumentData>;

const dataToSet = { project: SOON_PROJECT_ID, projects: { [SOON_PROJECT_ID]: true } };

export const setProjectRoll = async (app: FirebaseApp) => {
const adminApp = app.getInstance() as admin.app.App;
const db = adminApp.firestore();

for (const col of collections) {
let lastDoc: DocType = undefined;

do {
const batch = db.batch();
let query = db.collection(col).limit(500);
if (lastDoc) {
query = query.startAfter(lastDoc);
}
const snap: QuerySnap = await query.get();
lastDoc = last(snap.docs);

snap.docs.forEach((d) => {
const data = d.data() as BaseRecord;
if (!data.project) {
batch.update(d.ref, dataToSet);
}
});

await batch.commit();
} while (lastDoc);
}

for (const subCol of subCollections) {
let lastDoc: DocType = undefined;

do {
const batch = db.batch();
let query = db.collectionGroup(subCol).limit(500);
if (lastDoc) {
query = query.startAfter(lastDoc);
}
const snap: QuerySnap = await query.get();
lastDoc = last(snap.docs);

snap.docs.forEach((d) => {
const data = d.data() as BaseRecord;
if (!data.project) {
batch.update(d.ref, dataToSet);
}
});

await batch.commit();
} while (lastDoc);
}
};

export const roll = setProjectRoll;
2 changes: 1 addition & 1 deletion packages/functions/src/runtime/https/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ import { onRequest } from './https';
import { noAuth } from './middlewares';

exports[WEN_FUNC.createMember] = onRequest({
name: WEN_FUNC.updateMember,
name: WEN_FUNC.createMember,
schema: Joi.object({}),
middleware: noAuth,
handler: createMemberControl,
Expand Down
Loading

0 comments on commit e50dc7d

Please sign in to comment.