Skip to content

Commit

Permalink
add prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
bofeiw committed Oct 17, 2022
1 parent 3c58da9 commit b4e1e2b
Show file tree
Hide file tree
Showing 26 changed files with 3,405 additions and 3,027 deletions.
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
build/
.yarn
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ Cloud run instance for managing webhooks on Rowy
```
./deploy.sh --project [YOUR_PROJECT_ID]
```

2 changes: 1 addition & 1 deletion deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ while test $# -gt 0; do
return 1;
;;
esac
done
done

if [[ -z "$project_id" ]];
then
Expand Down
10 changes: 5 additions & 5 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"watch": ["src"],
"ext": ".ts,.js",
"ignore": [],
"exec": "node --es-module-specifier-resolution=node --loader ts-node/esm ./src/index.ts"
}
"watch": ["src"],
"ext": ".ts,.js",
"ignore": [],
"exec": "node --es-module-specifier-resolution=node --loader ts-node/esm ./src/index.ts"
}
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
"author": "ROWY INC",
"license": "Apache-2.0",
"dependencies": {
"@google-cloud/firestore": "^4.15.1",
"@google-cloud/logging": "^9.6.3",
"@google-cloud/secret-manager": "^3.10.1",
"@google-cloud/firestore": "^6.4.0",
"@google-cloud/logging": "^10.1.11",
"@google-cloud/secret-manager": "^4.1.3",
"@sendgrid/eventwebhook": "^7.4.5",
"axios": "^0.24.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"file-type": "^17.0.0",
"firebase-admin": "^10.0.0",
"firebase-admin": "^11.1.0",
"node-fetch": "^3.1.0",
"stripe": "^10.11.0",
"uuid": "^8.3.2"
Expand All @@ -44,6 +44,7 @@
"got": "^11.0.0",
"mocha": "^9.0.0",
"nodemon": "^2.0.12",
"prettier": "^2.7.1",
"rimraf": "^3.0.2",
"supertest": "^6.0.0",
"ts-node": "^10.4.0",
Expand Down
76 changes: 42 additions & 34 deletions src/consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { WEBHOOKS_DOC_PATH } from "./constants.js";
import { Logging } from "@google-cloud/logging";
import { getProjectId } from "./metadataService.js";
import verifiers from "./verifiers/index.js";
import fetch from "node-fetch";
import rowy from "./utils/index.js";
import installDependenciesIfMissing from "./utils/installDependenciesIfMissing.js";

Expand All @@ -14,7 +13,7 @@ type Endpoint = {
url: string;
path: string;
method: string;
type: "typeform" | "github" | "sendgrid" | "basic"| "webform";
type: "typeform" | "github" | "sendgrid" | "basic" | "webform";
tablePath: string;
conditions: (arg: {
req: Request;
Expand All @@ -25,8 +24,7 @@ type Endpoint = {
req: Request;
db: FirebaseFirestore.Firestore;
ref: FirebaseFirestore.CollectionReference;
res: {send:(v:any)=>void
sendStatus:(v:number)=>void};
res: { send: (v: any) => void; sendStatus: (v: number) => void };
}) => Promise<any>;
auth: {
secret: string;
Expand All @@ -35,29 +33,28 @@ type Endpoint = {
};

const { secrets } = rowy;
const _fetch = fetch;

let endpoints: null | any[] = null;
const setEndpoints = async (snapshot: DocumentSnapshot) => {
const docData = snapshot.data();
if (!docData) {
endpoints = [];
return;
}
const values = Object.values(docData);
if (values && values.length !== 0) {
endpoints = eval(`[${values.filter((v:string|null) => v).join(",")}]`)
;
endpoints = eval(`[${values.filter((v: string | null) => v).join(",")}]`);
} else endpoints = [];
};
db.doc(WEBHOOKS_DOC_PATH).onSnapshot(setEndpoints);

// See: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
const severities = {
"200": "INFO",
"404": "WARNING",
"401": "WARNING",
"500": "ERROR",
};

type HttpCode = "200" | "404" | "401" | "500";
const logEvent = async (request: Request, code: HttpCode, error?: string) => {
const { headers, url, params, body, query } = request;
Expand All @@ -74,20 +71,20 @@ const logEvent = async (request: Request, code: HttpCode, error?: string) => {
severity: severities[code],
};
// Prepares a log entry
const bodySize = new TextEncoder().encode(JSON.stringify(body)).length
const bodySize = new TextEncoder().encode(JSON.stringify(body)).length;
const entry = log.entry(metadata, {
headers,
url,
params,
body: bodySize > 250000 ? {v:"body too large"} : body,
body: bodySize > 250000 ? { v: "body too large" } : body,
query,
error,
});
return log.write(entry);
};

let cachedResponses:{endpoint:string,request:string,response:any}[] = [
]
let cachedResponses: { endpoint: string; request: string; response: any }[] =
[];

export const consumer = async (req: Request, res: Response) => {
const { params } = req;
Expand All @@ -106,35 +103,46 @@ export const consumer = async (req: Request, res: Response) => {
if (!verified) throw Error("401");
}
await installDependenciesIfMissing(
endpoint.conditions.toString(),
`condition ${endpoint.tablePath} of ${endpoint.url}`
)
endpoint.conditions.toString(),
`condition ${endpoint.tablePath} of ${endpoint.url}`
);
const condition = await endpoint.conditions({ req, db, ref });
if (!condition) return res.sendStatus(412);
let responseValue = undefined
const cachedResponse = cachedResponses.find(v=>v.endpoint===params.endpoint&&v.request===JSON.stringify(req.body))
if(cachedResponse && endpoint.cacheEnabled){
return res.send(cachedResponse.response)
let responseValue = undefined;
const cachedResponse = cachedResponses.find(
(v) =>
v.endpoint === params.endpoint && v.request === JSON.stringify(req.body)
);
if (cachedResponse && endpoint.cacheEnabled) {
return res.send(cachedResponse.response);
}

await installDependenciesIfMissing(
endpoint.parser.toString(),
`parser ${endpoint.tablePath} of ${endpoint.url}`
)
const newRow = await endpoint.parser({ req, db, ref,res:{send:(v)=>{responseValue=v},sendStatus:res.sendStatus} });
endpoint.parser.toString(),
`parser ${endpoint.tablePath} of ${endpoint.url}`
);
const newRow = await endpoint.parser({
req,
db,
ref,
res: {
send: (v) => {
responseValue = v;
},
sendStatus: res.sendStatus,
},
});
if (newRow) await Promise.all([ref.add(newRow), logEvent(req, "200")]);
else await logEvent(req, "200");
if(responseValue){
cachedResponses.push(
{
endpoint:params.endpoint,
request:JSON.stringify(req.body),
response:responseValue
}
)
res.send(responseValue)
}else{
res.sendStatus(200)
if (responseValue) {
cachedResponses.push({
endpoint: params.endpoint,
request: JSON.stringify(req.body),
response: responseValue,
});
res.send(responseValue);
} else {
res.sendStatus(200);
}
} catch (error: any) {
const errorCode = error.message.length === 3 ? error.message : "500";
Expand Down
10 changes: 5 additions & 5 deletions src/firebaseConfig.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Initialize Firebase Admin
import { initializeApp,applicationDefault } from 'firebase-admin/app'
import { getAuth } from 'firebase-admin/auth';
import { getFirestore } from 'firebase-admin/firestore';
import { getStorage } from 'firebase-admin/storage';
import { initializeApp, applicationDefault } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
import { getFirestore } from "firebase-admin/firestore";
import { getStorage } from "firebase-admin/storage";
initializeApp({
credential: applicationDefault(),
});
const db = getFirestore();
const auth = getAuth();
const storage = getStorage();
db.settings({ timestampsInSnapshots: true, ignoreUndefinedProperties: true });
export { db, auth, storage};
export { db, auth, storage };
39 changes: 22 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@

import { UserRecord } from 'firebase-admin/auth';
import { UserRecord } from "firebase-admin/auth";

import * as express from "express";
import { Request, Response } from 'express';
import { Request, Response } from "express";

import cors from "cors";
import { hasAnyRole, requireAuth } from './middleware/auth.js';
import { hasAnyRole, requireAuth } from "./middleware/auth.js";
import { configPublisher } from "./publisher.js";
import {consumer} from './consumer.js'
import { consumer } from "./consumer.js";
import rowyRedirect from "./rowyRedirect.js";
//import { metadataService } from './metadataService.js';
const app = express.default();
// json is the default content-type for POST requests
const rawBodySaver = (req:any, res:Response, buf:Buffer, encoding:BufferEncoding) => {
const rawBodySaver = (
req: any,
res: Response,
buf: Buffer,
encoding: BufferEncoding
) => {
if (
req.headers["user-agent"] === "Typeform Webhooks" &&
req.headers["typeform-signature"] &&
Expand All @@ -27,15 +31,17 @@ const rawBodySaver = (req:any, res:Response, buf:Buffer, encoding:BufferEncoding

const options = {
verify: rawBodySaver,
limit: '50mb',
extended: false
limit: "50mb",
extended: false,
};

app.use(express.json(options));// support json encoded bodies
app.use(express.json(options)); // support json encoded bodies
app.use(express.urlencoded({ extended: true })); // support encoded bodies
app.use(cors());

const functionWrapper = (fn:(req:Request,user:any)=>Promise<any>) => async (req:Request, res:Response) => {
const functionWrapper =
(fn: (req: Request, user: any) => Promise<any>) =>
async (req: Request, res: Response) => {
try {
const user: UserRecord = res.locals.user;
const data = await fn(req, user);
Expand All @@ -47,15 +53,15 @@ const functionWrapper = (fn:(req:Request,user:any)=>Promise<any>) => async (req:
};

// redirect /
app.get("/", rowyRedirect)
app.get("/", rowyRedirect);

// Webhooks
app.post(
"/publish",
requireAuth,
hasAnyRole(["ADMIN"]),
functionWrapper(configPublisher)
);
"/publish",
requireAuth,
hasAnyRole(["ADMIN"]),
functionWrapper(configPublisher)
);
app.post("/wh/:tablePath/:endpoint", consumer);
app.get("/wh/:tablePath/:endpoint", consumer);

Expand All @@ -66,6 +72,5 @@ app.listen(port, () => {
console.log(`RowyHooks: listening on port ${port}!`);
});


// Exports for testing purposes.
export { app };
8 changes: 4 additions & 4 deletions src/metadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export const getNumericProjectId = async () =>
.data;

export const getServiceAccountEmail = async () =>
(await axiosInstance.get(
"computeMetadata/v1/instance/service-accounts"
)).data.split("\n")[1];
(
await axiosInstance.get("computeMetadata/v1/instance/service-accounts")
).data.split("\n")[1];

export const generateServiceAccessToken = async (audience:string) =>
export const generateServiceAccessToken = async (audience: string) =>
(
await axiosInstance.get(
`computeMetadata/v1/instance/service-accounts/default/identity?audience=${audience}`
Expand Down
24 changes: 12 additions & 12 deletions src/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,21 @@ export const hasAnyRole =
const latestUser = await auth.getUser(user.uid);
if (!latestUser) {
return res.status(401).send("User not found");
}else{
const authDoubleCheck = roles.some((role) =>
latestUser.customClaims?.roles.includes(role)
);
if (authDoubleCheck) {
next();
} else {
res.status(401).send({
error: "Unauthorized",
message: "User does not have any of the required roles",
roles,
});
const authDoubleCheck = roles.some((role) =>
latestUser.customClaims?.roles.includes(role)
);
if (authDoubleCheck) {
next();
} else {
res.status(401).send({
error: "Unauthorized",
message: "User does not have any of the required roles",
roles,
});
}
}
}
}
} catch (err) {
res.status(401).send({ error: err });
}
Expand Down
Loading

0 comments on commit b4e1e2b

Please sign in to comment.