Postgraphile initial

This commit is contained in:
Kalle (Sendou) 2021-11-06 15:52:40 +02:00
parent f0c21b5d30
commit f0340a7772
5 changed files with 3795 additions and 0 deletions

144
postgraphile/index.ts Normal file
View File

@ -0,0 +1,144 @@
import { config } from "dotenv";
config();
import Fastify, { FastifyReply, FastifyRequest } from "fastify";
import {
postgraphile,
PostGraphileResponseFastify3,
PostGraphileResponse,
PostGraphileOptions,
} from "postgraphile";
const PORT = 4000;
// PostGraphile options; see https://www.graphile.org/postgraphile/usage-library/#api-postgraphilepgconfig-schemaname-options
const options: PostGraphileOptions = {
// pluginHook,
// appendPlugins: [MySubscriptionPlugin],
// pgSettings(req) {
// // Adding this to ensure that all servers pass through the request in a
// // good enough way that we can extract headers.
// // CREATE FUNCTION current_user_id() RETURNS text AS $$ SELECT current_setting('graphile.test.x-user-id', TRUE); $$ LANGUAGE sql STABLE;
// return {
// 'graphile.test.x-user-id':
// req.headers['x-user-id'] ||
// // `normalizedConnectionParams` comes from websocket connections, where
// // the headers often cannot be customized by the client.
// (req as any).normalizedConnectionParams?.['x-user-id'],
// };
// },
watchPg: true,
graphiql: true,
enhanceGraphiql: true,
subscriptions: true,
dynamicJson: true,
setofFunctionsContainNulls: false,
ignoreRBAC: false,
showErrorStack: "json",
extendedErrors: ["hint", "detail", "errcode"],
allowExplain: true,
legacyRelations: "omit",
// exportGqlSchemaPath: `${__dirname}/schema.graphql`,
sortExport: true,
};
const middleware = postgraphile(process.env.DATABASE_URL, "sendou_ink", {
...options,
// pgSettings(req) {
// // Adding this to ensure that all servers pass through the request in a
// // good enough way that we can extract headers.
// // CREATE FUNCTION current_user_id() RETURNS text AS $$ SELECT current_setting('graphile.test.x-user-id', TRUE); $$ LANGUAGE sql STABLE;
// return {
// 'graphile.test.x-user-id':
// // In GraphiQL, open console and enter `document.cookie = "userId=3"` to become user 3.
// (req._fastifyRequest as FastifyRequest)?.cookies.userId ||
// // `normalizedConnectionParams` comes from websocket connections, where
// // the headers often cannot be customized by the client.
// (req as any).normalizedConnectionParams?.['x-user-id'],
// };
// },
});
/******************************************************************************/
// These middlewares aren't needed; we just add them to make sure that
// PostGraphile still works correctly with them in place.
// import fastifyCompression from 'fastify-compress';
// fastify.register(fastifyCompression, { threshold: 0, inflateIfDeflated: false });
// import fastifyCookie from 'fastify-cookie';
// fastify.register(fastifyCookie, { secret: 'USE_A_SECURE_SECRET!' });
/******************************************************************************/
const fastify = Fastify({ logger: true });
/**
* Converts a PostGraphile route handler into a Fastify request handler.
*/
const convertHandler =
(handler: (res: PostGraphileResponse) => Promise<void>) =>
(request: FastifyRequest, reply: FastifyReply) =>
handler(new PostGraphileResponseFastify3(request, reply));
// IMPORTANT: do **NOT** change these routes here; if you want to change the
// routes, do so in PostGraphile options. If you change the routes here only
// then GraphiQL won't know where to find the GraphQL endpoint and the GraphQL
// endpoint won't know where to indicate the EventStream for watch mode is.
// (There may be other problems too.)
// OPTIONS requests, for CORS/etc
fastify.options(
middleware.graphqlRoute,
convertHandler(middleware.graphqlRouteHandler)
);
// This is the main middleware
fastify.post(
middleware.graphqlRoute,
convertHandler(middleware.graphqlRouteHandler)
);
// GraphiQL, if you need it
if (middleware.options.graphiql) {
if (middleware.graphiqlRouteHandler) {
fastify.head(
middleware.graphiqlRoute,
convertHandler(middleware.graphiqlRouteHandler)
);
fastify.get(
middleware.graphiqlRoute,
convertHandler(middleware.graphiqlRouteHandler)
);
}
// Remove this if you don't want the PostGraphile logo as your favicon!
if (middleware.faviconRouteHandler) {
fastify.get("/favicon.ico", convertHandler(middleware.faviconRouteHandler));
}
}
// If you need watch mode, this is the route served by the
// X-GraphQL-Event-Stream header; see:
// https://github.com/graphql/graphql-over-http/issues/48
if (middleware.options.watchPg) {
if (middleware.eventStreamRouteHandler) {
fastify.options(
middleware.eventStreamRoute,
convertHandler(middleware.eventStreamRouteHandler)
);
fastify.get(
middleware.eventStreamRoute,
convertHandler(middleware.eventStreamRouteHandler)
);
}
}
fastify.listen(PORT, (err, address) => {
if (err) {
fastify.log.error(String(err));
process.exit(1);
}
fastify.log.info(
`PostGraphiQL available at ${address}${middleware.graphiqlRoute} 🚀`
);
});

View File

@ -0,0 +1,53 @@
-- dev only
drop schema if exists sendou_ink cascade;
drop schema if exists sendou_ink_private cascade;
create schema sendou_ink;
create schema sendou_ink_private;
create function sendou_ink_private.set_updated_at() returns trigger as $$
begin
new.updated_at := current_timestamp;
return new;
end;
$$ language plpgsql;
create table sendou_ink.account (
id serial primary key,
discord_username text not null,
discord_discriminator text not null,
discord_avatar text,
twitch text,
twitter text,
youtube_id text,
youtube_name text,
created_at timestamp default now(),
updated_at timestamp default now()
);
comment on table sendou_ink.account is 'User containing all information automatically fetched on log in.';
comment on column sendou_ink.account.id is 'The primary unique identifier for the user.';
comment on column sendou_ink.account.discord_username is 'Users username on Discord.';
comment on column sendou_ink.account.discord_discriminator is 'Users discriminator on Discord. (e.g. "0043")';
comment on column sendou_ink.account.discord_avatar is 'Users Discord avatar hash.';
comment on column sendou_ink.account.twitch is 'Users username on Twitch.';
comment on column sendou_ink.account.twitter is 'Users username on Twitter.';
comment on column sendou_ink.account.youtube_id is 'Users id on YouTube.';
comment on column sendou_ink.account.youtube_name is 'Users name on YouTube (YouTube partners only).';
comment on column sendou_ink.account.created_at is 'The time this user was created.';
create function sendou_ink.account_discord_full_username(account sendou_ink.account) returns text as $$
select account.discord_username || '#' || account.discord_discriminator
$$ language sql stable;
comment on function sendou_ink.account_discord_full_username(sendou_ink.account) is 'A users full username on Discord consisting of username and discriminator joined together with #.';
create trigger account_updated_at before update
on sendou_ink.account
for each row
execute procedure sendou_ink_private.set_updated_at();
-- seed for dev
insert into sendou_ink.account (discord_username, discord_discriminator, discord_avatar, twitch, twitter, youtube_id, youtube_name)
values ('Sendou', '0043', 'fcfd65a3bea598905abb9ca25296816b', 'Sendou', 'Sendouc', 'UCWbJLXByvsfQvTcR4HLPs5Q', 'Sendou')

View File

@ -0,0 +1,18 @@
import { migrate } from "postgres-migrations";
async function main() {
const dbConfig = {
// TODO: env vars
database: "sendou_ink_postgraphile",
user: "sendou",
password: "password",
host: "localhost",
port: 5432,
ensureDatabaseExists: true,
defaultDatabase: "postgres",
};
await migrate(dbConfig, "migrations");
}
main().then(() => console.log("Migrations done"));

3563
postgraphile/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

17
postgraphile/package.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "@sendou.ink/postgraphile",
"version": "1.0.0",
"dependencies": {
"fastify": "^3.22.1",
"postgraphile": "^4.12.5",
"postgres-migrations": "^5.3.0"
},
"devDependencies": {
"dotenv": "^10.0.0",
"ts-node-dev": "^1.1.8"
},
"scripts": {
"dev": "ts-node-dev index.ts",
"migrate": "node migrations/index.mjs"
}
}