Last active
December 17, 2020 09:47
-
-
Save pszafer/12b91a4cd62aee2736978cdd5de007a2 to your computer and use it in GitHub Desktop.
postgraphile server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PgUpsertPlugin from "./PgUpsertPlugin"; | |
import PostGraphileFulltextFilterPlugin from "./PostgraphileFullTextFilterPlugin" | |
import PgSimplifyInflectorPlugin from "@graphile-contrib/pg-simplify-inflector"; | |
import ConnectionFilterPlugin from "postgraphile-plugin-connection-filter"; | |
import PgOrderByRelatedPlugin from "@graphile-contrib/pg-order-by-related"; | |
import PgOrderByMultiColumnIndexPlugin from "./PgOrderByMultiColumnIndexPlugin"; | |
import { SHAREDTOKEN, CORS_OPTIONS, FORMTOKEN, hosts, adminPayload, workerPayload } from "../config"; | |
import express from "express"; | |
import UnauthorizedError from "./UnauthorizedError"; | |
import { postgraphile, makePluginHook } from "postgraphile"; | |
import { OAuth2Client } from "google-auth-library"; | |
import bearerToken from "express-bearer-token"; | |
import cors from "cors"; | |
import PgPubsub from '@graphile/pg-pubsub'; | |
import { makeExtendSchemaPlugin, gql } from "graphile-utils"; | |
const pluginHook = makePluginHook([PgPubsub]); | |
let requestCount = 0; | |
const morgan = require('morgan'); | |
const compression = require("compression") | |
const bearer = bearerToken() | |
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID); | |
const port = process.env.PORT || 8081; | |
const host = `localhost:${port}`; | |
const referers = [...hosts.map(x => (`http://${x}:${port}/graphiql`))] | |
const extractToken = ({token, connectionParams: { authToken } = {}}) => token ? token : authToken ? authToken : null | |
const asyncJWT = async (req, res, next) => { | |
++requestCount; | |
console.log(`Request number: ${requestCount}`) | |
var wsProto = false; | |
if (req.headers['sec-websocket-protocol']){ | |
wsProto= true; | |
} | |
const idToken = extractToken(req); | |
if (idToken === FORMTOKEN) { | |
req.payload = workerPayload(req) | |
req.form = true; | |
console.log("Finishing form NextJS") | |
return next(); | |
} | |
if ( | |
(referers.includes(req.headers.referer) && SHAREDTOKEN == idToken || host === req.headers.host) | |
) { | |
req.payload = adminPayload; | |
req.admin = true; | |
console.log("Admin, giving access.") | |
return next(); | |
} | |
if (idToken) { | |
console.log("Ticker", idToken) | |
try { | |
const ticket = await client.verifyIdToken({ | |
idToken: idToken, | |
audience: process.env.GOOGLE_CLIENT_ID, | |
}); | |
const payload = ticket.getPayload(); | |
if (payload["hd"] === process.env.GOOGLE_DOMAIN) { | |
req["payload"] = payload; | |
console.log("Google user. Giving access.") | |
return next(); | |
} | |
} catch (error) { | |
console.error("Error with token.") | |
return next( | |
new UnauthorizedError("too_old_token", { | |
message: "Token is too old " + error, | |
}) | |
); | |
} | |
} | |
console.error("No credentials.") | |
return next( | |
new UnauthorizedError("credentials_bad_format", { | |
message: "Format is Authorization: Bearer [token]", | |
}) | |
); | |
}; | |
const authErrors = (err, req, res, next) => { | |
if ( | |
err.name === "UnauthorizedError" || | |
err.name === "UnhandledPromiseRejectionWarning" | |
) { | |
res.status(err.status).json({ errors: [{ message: err.message }] }); | |
res.end(); | |
} | |
}; | |
const errorsLevel = ["hint", "detail", "errcode", "notice"]; | |
// const errorsLevel = [ "severity", "code", "detail", "hint", "position", | |
// "internalPosition", "internalQuery", "where", "schema", | |
// "table", "column", "dataType", "constraint", "file", "line", "routine" ] | |
const postgraphileOptions = { | |
pgSettings: (req) => { | |
const settings = {}; | |
if (req.payload) { | |
settings["role"] = req.admin ? "admin" : req.form ? "workperson" : "person"; | |
settings["user.sub"] = req.payload["sub"]; | |
settings["user.firstname"] = req.payload["given_name"]; | |
settings["user.lastname"] = req.payload["family_name"]; | |
settings["user.email"] = req.payload["email"]; | |
} else { | |
settings["role"] = "intranet_anonymous"; | |
} | |
return settings; | |
}, | |
pluginHook, | |
subscriptions: true, | |
exportGqlSchemaPath: "schema.graphql", | |
watchPg: true, | |
ignoreRBAC: false, | |
graphiql: true, | |
enhanceGraphiql: true, | |
enableQueryBatching: true, | |
showErrorStack: "json", | |
dynamicJson: true, | |
setofFunctionsContainNulls: false, | |
websocketMiddlewares: | |
[ | |
bearer, | |
asyncJWT | |
], | |
allowExplain(req) { | |
return true; | |
}, | |
disableQueryLog: true, | |
extendedErrors: ["hint", "detail", "errcode", "notice"], | |
appendPlugins: [ | |
MySubscriptionPlugin, | |
PgSimplifyInflectorPlugin, | |
ConnectionFilterPlugin, | |
PgOrderByRelatedPlugin, | |
PgOrderByMultiColumnIndexPlugin, | |
PostGraphileFulltextFilterPlugin, | |
PgUpsertPlugin, | |
], | |
graphileBuildOptions: { | |
orderByNullsLast: true, | |
connectionFilterAllowNullInput: true, // default: false | |
connectionFilterAllowEmptyObjectInput: true, | |
connectionFilterRelations: true, | |
connectionFilterSetofFunctions: true, | |
connectionFilterComputedColumns: true | |
}, | |
} | |
const postgraphileMiddleware = postgraphile(process.env.DATABASE_URL, process.env.DB_SCHEMA, postgraphileOptions) | |
const app = express(); | |
app.use(cors(CORS_OPTIONS)); | |
app.use(compression()) | |
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"')) | |
app.use("/graphql", bearer); | |
app.use("/graphql", asyncJWT); | |
app.use("/graphql", authErrors); | |
app.use(postgraphileMiddleware); | |
app.listen(port, () => { | |
console.log(`Pricing service listening on http://localhost:${port}/graphiql`); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment