mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-15 07:43:44 -05:00
Postgraphile initial
This commit is contained in:
parent
f0c21b5d30
commit
f0340a7772
144
postgraphile/index.ts
Normal file
144
postgraphile/index.ts
Normal 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} 🚀`
|
||||
);
|
||||
});
|
||||
53
postgraphile/migrations/0001_initial.sql
Normal file
53
postgraphile/migrations/0001_initial.sql
Normal 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 'User’s username on Discord.';
|
||||
comment on column sendou_ink.account.discord_discriminator is 'User’s discriminator on Discord. (e.g. "0043")';
|
||||
comment on column sendou_ink.account.discord_avatar is 'User’s Discord avatar hash.';
|
||||
comment on column sendou_ink.account.twitch is 'User’s username on Twitch.';
|
||||
comment on column sendou_ink.account.twitter is 'User’s username on Twitter.';
|
||||
comment on column sendou_ink.account.youtube_id is 'User’s id on YouTube.';
|
||||
comment on column sendou_ink.account.youtube_name is 'User’s 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 user’s 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')
|
||||
18
postgraphile/migrations/index.mjs
Normal file
18
postgraphile/migrations/index.mjs
Normal 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
3563
postgraphile/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
postgraphile/package.json
Normal file
17
postgraphile/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user