Cleaned up things I decided to not use
This commit is contained in:
		
							
								
								
									
										714
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										714
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -18,7 +18,6 @@ | ||||
|   "dependencies": { | ||||
|     "@holdyourwaffle/express-session": "^1.16.2", | ||||
|     "@types/pug": "^2.0.4", | ||||
|     "apollo-server-express": "^2.9.7", | ||||
|     "argon2": "^0.24.1", | ||||
|     "cookie-parser": "^1.4.4", | ||||
|     "csurf": "^1.10.0", | ||||
| @@ -26,8 +25,6 @@ | ||||
|     "dotenv": "^8.2.0", | ||||
|     "express": "^4.17.1", | ||||
|     "express-pino-logger": "^4.0.0", | ||||
|     "graphql": "^14.5.8", | ||||
|     "graphql-tools": "^4.0.5", | ||||
|     "helmet": "^3.21.2", | ||||
|     "http-errors": "^1.7.3", | ||||
|     "luxon": "^1.19.3", | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| DROP TYPE task_schedule; | ||||
| @@ -1 +0,0 @@ | ||||
| CREATE TYPE task_schedule AS ENUM('once', 'daily', 'weekly', 'monthly', 'yearly'); | ||||
| @@ -3,12 +3,9 @@ CREATE TABLE tasks ( | ||||
|     owner integer NOT NULL REFERENCES users(id), | ||||
|     name text NOT NULL, | ||||
|     notes text, | ||||
|     schedule task_schedule NOT NULL DEFAULT 'daily', | ||||
|     min_frequency integer, | ||||
|     max_frequency integer, | ||||
|     schedule jsonb NOT NULL DEFAULT '{}', | ||||
|     created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     CHECK(min_frequency IS NULL OR max_frequency IS NULL OR max_frequency > min_frequency) | ||||
|     updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP | ||||
| ); | ||||
|  | ||||
| CREATE TRIGGER set_tasks_updated BEFORE UPDATE ON tasks FOR EACH ROW EXECUTE PROCEDURE set_updated_timestamp(); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| CREATE TABLE "sessions" ( | ||||
|     "sid" text NOT NULL COLLATE "default", | ||||
| 	"session" json NOT NULL, | ||||
| 	"session" jsonb NOT NULL, | ||||
|     "expires_at" timestamptz NOT NULL, | ||||
|     "created_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||||
|     "updated_at" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP | ||||
|   | ||||
| @@ -1,36 +0,0 @@ | ||||
| // Copyright (C) 2019 ModZero <modzero@modzero.xyz> | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as | ||||
| // published by the Free Software Foundation, either version 3 of the | ||||
| // License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| import resolvers from "@kredens/api/resolvers"; | ||||
| import typeDefs from "@kredens/api/typeDefs"; | ||||
| import { ApolloServer, AuthenticationError } from "apollo-server-express"; | ||||
|  | ||||
| export function server() { | ||||
|   return new ApolloServer({ | ||||
|     context: async req => { | ||||
|       const user = req.req.user; | ||||
|  | ||||
|       if (!user) { | ||||
|         throw new AuthenticationError("you must be logged in"); | ||||
|       } | ||||
|  | ||||
|       return { | ||||
|         user | ||||
|       }; | ||||
|     }, | ||||
|     resolvers, | ||||
|     typeDefs | ||||
|   }); | ||||
| } | ||||
| @@ -1,226 +0,0 @@ | ||||
| // Copyright (C) 2019 ModZero <modzero@modzero.xyz> | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as | ||||
| // published by the Free Software Foundation, either version 3 of the | ||||
| // License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| import { db } from "@kredens/db"; | ||||
| import * as models from "@kredens/db/models"; | ||||
| import { IResolvers } from "graphql-tools"; | ||||
| import { Kind } from "graphql/language"; | ||||
| import { GraphQLScalarType, GraphQLScalarTypeConfig } from "graphql/type"; | ||||
| import { DateTime } from "luxon"; | ||||
| import { Maybe } from "monet"; | ||||
|  | ||||
| const dateTimeConfig: GraphQLScalarTypeConfig<DateTime, string> = { | ||||
|   name: "DateTime", | ||||
|   description: "Date custom scalar type", | ||||
|   serialize(value) { | ||||
|     return (value as DateTime).toISO(); | ||||
|   }, | ||||
|   parseValue(value) { | ||||
|     return DateTime.fromISO(value as string); | ||||
|   }, | ||||
|   parseLiteral(ast) { | ||||
|     if (ast.kind === Kind.STRING) { | ||||
|       return DateTime.fromISO(ast.value); | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| interface Context { | ||||
|   user?: models.User; | ||||
| } | ||||
|  | ||||
| interface Node { | ||||
|   ID: string; | ||||
| } | ||||
|  | ||||
| interface User extends Node { | ||||
|   email?: string; | ||||
|   tasks?: Connection<Task>; | ||||
| } | ||||
|  | ||||
| type ScheduleType = "ONCE" | "DAILY" | "WEEKLY" | "MONTHLY" | "YEARLY"; | ||||
|  | ||||
| interface Task extends Node { | ||||
|   name: string; | ||||
|   notes?: string; | ||||
|   schedule: ScheduleType; | ||||
|   minFrequency?: number; | ||||
|   maxFrequency?: number; | ||||
|   createdAt: DateTime; | ||||
| } | ||||
|  | ||||
| interface PaginationArguments { | ||||
|   after?: string; | ||||
|   first?: number; | ||||
|   before?: string; | ||||
|   last?: number; | ||||
| } | ||||
|  | ||||
| interface PageInfo { | ||||
|   hasNextPage: boolean; | ||||
|   hasPrevPage: boolean; | ||||
|   startCursor?: string; | ||||
|   endCursor?: string; | ||||
| } | ||||
|  | ||||
| interface Edge<T extends Node> { | ||||
|   cursor: string; | ||||
|   node?: T; | ||||
| } | ||||
|  | ||||
| interface Connection<T extends Node> { | ||||
|   edges?: Array<Edge<T>>; | ||||
|   pageInfo: PageInfo; | ||||
| } | ||||
|  | ||||
| enum NodeKind { | ||||
|   User = "USER", | ||||
|   Task = "TASK" | ||||
| } | ||||
|  | ||||
| const nodeKindFrom = (kind: string) => { | ||||
|   const keys = Object.keys(NodeKind).filter(x => NodeKind[x] === kind); | ||||
|   return keys.length > 0 | ||||
|     ? Maybe.some<NodeKind>(NodeKind[keys[0]]) | ||||
|     : Maybe.none<NodeKind>(); | ||||
| }; | ||||
|  | ||||
| interface ID { | ||||
|   kind: NodeKind; | ||||
|   id: number; | ||||
| } | ||||
|  | ||||
| const getId = (id: string) => { | ||||
|   const [kindStr, idStr] = Buffer.from(id, "base64") | ||||
|     .toString() | ||||
|     .split(":"); | ||||
|   const kind = nodeKindFrom(kindStr); | ||||
|  | ||||
|   return kind.flatMap<ID>(k => { | ||||
|     const actualId = parseInt(idStr, 10); | ||||
|  | ||||
|     return isNaN(actualId) | ||||
|       ? Maybe.none() | ||||
|       : Maybe.some({ id: actualId, kind: k }); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const getIdOfKind = (id: string, kind: NodeKind) => | ||||
|   getId(id).filter(i => i.kind === kind); | ||||
|  | ||||
| const buildId = (kind: NodeKind, id: number) => | ||||
|   Buffer.from(`${kind}:${id}`).toString("base64"); | ||||
|  | ||||
| const scheduleTypeMap: { [id: string]: ScheduleType } = { | ||||
|   [models.ScheduleType.Once]: "ONCE", | ||||
|   [models.ScheduleType.Daily]: "DAILY", | ||||
|   [models.ScheduleType.Weekly]: "WEEKLY", | ||||
|   [models.ScheduleType.Monthly]: "MONTHLY", | ||||
|   [models.ScheduleType.Yearly]: "YEARLY" | ||||
| }; | ||||
|  | ||||
| async function userTasks( | ||||
|   user: { id: string }, | ||||
|   args: PaginationArguments | ||||
| ): Promise<Connection<Task>> { | ||||
|   return db.task<Connection<Task>>(async t => { | ||||
|     const after = | ||||
|       args.after && | ||||
|       Maybe.some(args.before) | ||||
|         .flatMap(id => getIdOfKind(id, NodeKind.Task)) | ||||
|         .some().id; | ||||
|     const before = | ||||
|       args.before && | ||||
|       Maybe.fromFalsy(args.before) | ||||
|         .flatMap(id => getIdOfKind(id, NodeKind.Task)) | ||||
|         .some().id; | ||||
|     const userId = getId(user.id).some().id; | ||||
|  | ||||
|     let rows = | ||||
|       args.first || !(args.first || args.last) | ||||
|         ? await t.tasks.list(userId, args.first, after, before) | ||||
|         : (await t.tasks.listReverse( | ||||
|             userId, | ||||
|             args.last, | ||||
|             after, | ||||
|             before | ||||
|           )).reverse(); | ||||
|  | ||||
|     if (args.first && args.last) { | ||||
|       rows = rows.slice(-1 * args.last); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       edges: rows.map(row => ({ | ||||
|         cursor: buildId(NodeKind.Task, row.id), | ||||
|         node: { | ||||
|           ID: buildId(NodeKind.Task, row.id), | ||||
|           createdAt: row.createdAt, | ||||
|           maxFrequency: row.maxFrequency, | ||||
|           minFrequency: row.minFrequency, | ||||
|           name: row.name, | ||||
|           notes: row.notes, | ||||
|           schedule: scheduleTypeMap[row.schedule] | ||||
|         } | ||||
|       })), | ||||
|       pageInfo: { | ||||
|         hasNextPage: | ||||
|           rows.length > 0 ? await t.tasks.hasNext(userId, rows[0].id) : false, | ||||
|         hasPrevPage: | ||||
|           rows.length > 0 | ||||
|             ? await t.tasks.hasPrev(userId, rows[rows.length - 1].id) | ||||
|             : false, | ||||
|         startCursor: | ||||
|           rows.length > 0 ? buildId(NodeKind.Task, rows[0].id) : null, | ||||
|         endCursor: | ||||
|           rows.length > 0 | ||||
|             ? buildId(NodeKind.Task, rows[rows.length - 1].id) | ||||
|             : null | ||||
|       } | ||||
|     }; | ||||
|   }); | ||||
| } | ||||
|  | ||||
| const resolvers: IResolvers<any, Context> = { | ||||
|   DateTime: new GraphQLScalarType(dateTimeConfig), | ||||
|   Query: { | ||||
|     hello: () => `Hello, world!`, | ||||
|     migrations: () => db.migrations.applied(), | ||||
|     user: async (parent: any, { id }: { id?: string }, context) => | ||||
|       Maybe.fromFalsy(id) | ||||
|         .orElse(Maybe.some(buildId(NodeKind.User, context.user.id))) | ||||
|         .flatMap(i => getId(i)) | ||||
|         .filter(i => i.kind === NodeKind.User) | ||||
|         .filter(i => i.id === context.user.id) | ||||
|         .map(i => | ||||
|           db.users.details(i.id).then(user => | ||||
|             user | ||||
|               .map(u => ({ | ||||
|                 email: u.email, | ||||
|                 id: buildId(NodeKind.User, u.id) | ||||
|               })) | ||||
|               .orNull() | ||||
|           ) | ||||
|         ) | ||||
|         .orNull() | ||||
|   }, | ||||
|   User: { | ||||
|     tasks: userTasks | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export default resolvers; | ||||
| @@ -1,79 +0,0 @@ | ||||
| // Copyright (C) 2019 ModZero <modzero@modzero.xyz> | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU Affero General Public License as | ||||
| // published by the Free Software Foundation, either version 3 of the | ||||
| // License, or (at your option) any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| // GNU Affero General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU Affero General Public License | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| import { gql } from "apollo-server-express"; | ||||
|  | ||||
| export default gql` | ||||
|   type Query { | ||||
|     "A simple type for getting started" | ||||
|     node(id: ID!): Node | ||||
|     hello: String | ||||
|     migrations: [Migration]! | ||||
|     user(id: ID): User | ||||
|   } | ||||
|  | ||||
|   interface Node { | ||||
|     id: ID! | ||||
|   } | ||||
|  | ||||
|   type Migration implements Node { | ||||
|     id: ID! | ||||
|     name: String! | ||||
|     appliedAt: DateTime! | ||||
|   } | ||||
|  | ||||
|   type User implements Node { | ||||
|     id: ID! | ||||
|     email: String! | ||||
|     tasks(after: String, first: Int, before: String, last: Int): TaskConnection | ||||
|   } | ||||
|  | ||||
|   type Task implements Node { | ||||
|     id: ID! | ||||
|     name: String! | ||||
|     notes: String | ||||
|     schedule: ScheduleType! | ||||
|     minFrequency: Int | ||||
|     maxFrequency: Int | ||||
|     createdAt: DateTime! | ||||
|   } | ||||
|  | ||||
|   type TaskConnection { | ||||
|     edges: [TaskEdge] | ||||
|     pageInfo: PageInfo! | ||||
|   } | ||||
|  | ||||
|   type TaskEdge { | ||||
|     cursor: String! | ||||
|     node: Task | ||||
|   } | ||||
|  | ||||
|   type PageInfo { | ||||
|     hasNextPage: Boolean! | ||||
|     hasPrevPage: Boolean! | ||||
|     startCursor: String | ||||
|     endCursor: String | ||||
|   } | ||||
|  | ||||
|   scalar DateTime | ||||
|  | ||||
|   enum ScheduleType { | ||||
|     ONCE | ||||
|     DAILY | ||||
|     WEEKLY | ||||
|     MONTHLY | ||||
|     YEARLY | ||||
|   } | ||||
| `; | ||||
| @@ -19,14 +19,14 @@ import { IDatabase, IMain } from "pg-promise"; | ||||
|  | ||||
| function rowToTask(row: any): Task { | ||||
|   return { | ||||
|     createdAt: row.created_at, | ||||
|     id: +row.id, | ||||
|     maxFrequency: +row.maxFrequency, | ||||
|     minFrequency: +row.minFrequency, | ||||
|     owner: +row.owner, | ||||
|     name: row.name, | ||||
|     notes: row.notes, | ||||
|     owner: +row.owner, | ||||
|     schedule: row.schedule as ScheduleType | ||||
|     schedule: row.schedule as ScheduleType, | ||||
|     minFrequency: +row.minFrequency, | ||||
|     maxFrequency: +row.maxFrequency, | ||||
|     createdAt: row.created_at | ||||
|   }; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -14,7 +14,6 @@ | ||||
| // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| import session, { SessionOptions } from "@holdyourwaffle/express-session"; | ||||
| import { server as graphqlServer } from "@kredens/api"; | ||||
| import { authMiddleware } from "@kredens/auth"; | ||||
| import { db } from "@kredens/db"; | ||||
| import logger from "@kredens/logger"; | ||||
| @@ -59,10 +58,10 @@ async function main() { | ||||
|   app.use(express.urlencoded({ extended: false })); | ||||
|   app.use(cookieParser()); | ||||
|   const sessionOptions: SessionOptions = { | ||||
|     resave: false, | ||||
|     saveUninitialized: false, | ||||
|     secret: process.env.SECRET, | ||||
|     store: new PgStore() | ||||
|     store: new PgStore(), | ||||
|     resave: false, | ||||
|     saveUninitialized: false | ||||
|   }; | ||||
|  | ||||
|   if (app.get("env") === "production") { | ||||
| @@ -73,11 +72,6 @@ async function main() { | ||||
|   app.use("/bootstrap", bootstrapRouter); | ||||
|  | ||||
|   app.use(authMiddleware()); | ||||
|   const apiServer = graphqlServer(); | ||||
|   apiServer.applyMiddleware({ | ||||
|     app, | ||||
|     path: "/graphql" | ||||
|   }); | ||||
|  | ||||
|   app.use(csrf()); | ||||
|  | ||||
| @@ -99,7 +93,7 @@ async function main() { | ||||
|   const port = 3000; | ||||
|   app.listen(port, () => | ||||
|     logger.info("Example app listening", { | ||||
|       uri: `http://localhost:${port}${apiServer.graphqlPath}` | ||||
|       uri: `http://localhost:${port}` | ||||
|     }) | ||||
|   ); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user