Cleaned up things I decided to not use
This commit is contained in:
parent
461cebe009
commit
bff5c60a4e
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}`
|
||||
})
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user