diff --git a/sql/users/details.sql b/sql/users/details.sql
new file mode 100644
index 0000000..346d01e
--- /dev/null
+++ b/sql/users/details.sql
@@ -0,0 +1 @@
+SELECT email FROM users WHERE id=$1;
\ No newline at end of file
diff --git a/src/api.ts b/src/api.ts
index 14a2508..7ba9809 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -14,7 +14,7 @@
// along with this program. If not, see .
import { db } from "@kredens/db";
-import { ApolloServer, gql } from "apollo-server-express";
+import { ApolloServer, AuthenticationError, gql } from "apollo-server-express";
import { Kind } from "graphql/language";
import { GraphQLScalarType, GraphQLScalarTypeConfig } from "graphql/type";
import { DateTime } from "luxon";
@@ -67,6 +67,17 @@ const resolvers = {
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
});
diff --git a/src/auth.ts b/src/auth.ts
new file mode 100644
index 0000000..a714457
--- /dev/null
+++ b/src/auth.ts
@@ -0,0 +1,40 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
+import { db } from "@kredens/db";
+import { User } from "@kredens/db/models";
+import express from "express";
+import { None } from "monet";
+
+export const getUser = async (req: express.Request) =>
+ req.session.userID ? db.users.details(req.session.userID) : None();
+
+export const authMiddleware: () => express.Handler = () => async (
+ req,
+ res,
+ next
+) => {
+ if (req.session.userID) {
+ const user = await getUser(req);
+
+ if (user.isSome()) {
+ req.user = user.some();
+ } else {
+ delete req.session.userID;
+ }
+ }
+
+ next();
+};
diff --git a/src/custom.d.ts b/src/custom.d.ts
new file mode 100644
index 0000000..5f37445
--- /dev/null
+++ b/src/custom.d.ts
@@ -0,0 +1,24 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
+import { User } from "@kredens/db/models";
+
+declare global {
+ namespace Express {
+ interface Request {
+ user?: User;
+ }
+ }
+}
diff --git a/src/db/index.ts b/src/db/index.ts
index 0746025..96d0098 100644
--- a/src/db/index.ts
+++ b/src/db/index.ts
@@ -18,7 +18,7 @@ import {
MigrationRepository,
UserRepository
} from "@kredens/db/repos";
-import monitor from "pg-monitor";
+
import pgPromise, { IDatabase, IInitOptions } from "pg-promise";
type ExtendedProtocol = IDatabase & Extensions;
@@ -31,6 +31,11 @@ const initOptions: IInitOptions = {
};
const pgp: pgPromise.IMain = pgPromise(initOptions);
-monitor.attach(initOptions);
+
+if (process.env.NODE_ENV !== "production") {
+ // tslint:disable-next-line:no-implicit-dependencies
+ import("pg-monitor").then(monitor => monitor.attach(initOptions));
+}
+
const db: ExtendedProtocol = pgp(process.env.PG_CONNECTION_STRING);
export { db, pgp };
diff --git a/src/db/models.ts b/src/db/models.ts
index d15ff61..4305c16 100644
--- a/src/db/models.ts
+++ b/src/db/models.ts
@@ -20,3 +20,8 @@ export interface Migration {
name: string;
applied_at: DateTime;
}
+
+export interface User {
+ id: number;
+ email: string;
+}
diff --git a/src/db/repos/users.ts b/src/db/repos/users.ts
index e10db52..0daee17 100644
--- a/src/db/repos/users.ts
+++ b/src/db/repos/users.ts
@@ -17,6 +17,7 @@ import { users as sql } from "@kredens/db/sql";
import argon2 from "argon2";
import { Maybe, None, Some } from "monet";
import { IDatabase, IMain } from "pg-promise";
+import { User } from "@kredens/db/models";
export class UserRepository {
private db: IDatabase;
@@ -47,4 +48,12 @@ export class UserRepository {
.one(sql.create, [email, encryptedPassword])
.then((user: { id: number }) => +user.id);
}
+
+ public async details(id: number): Promise> {
+ return this.db
+ .oneOrNone(sql.details, [id])
+ .then((user: { email: string }) =>
+ user !== null ? Some({ ...user, id }) : None()
+ );
+ }
}
diff --git a/src/db/sql/index.ts b/src/db/sql/index.ts
index c3ca587..edbb650 100644
--- a/src/db/sql/index.ts
+++ b/src/db/sql/index.ts
@@ -37,6 +37,7 @@ const migrations = {
const users = {
create: sql("users/create.sql"),
+ details: sql("users/details.sql"),
login: sql("users/login.sql")
};
diff --git a/src/frontend/index.ts b/src/frontend/index.ts
index 6e714f4..efd269c 100644
--- a/src/frontend/index.ts
+++ b/src/frontend/index.ts
@@ -1,3 +1,18 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
import Vue from "vue";
import App from "./App.vue";
diff --git a/src/frontend/vue-shim.d.ts b/src/frontend/vue-shim.d.ts
index 0660bd6..cdcc639 100644
--- a/src/frontend/vue-shim.d.ts
+++ b/src/frontend/vue-shim.d.ts
@@ -1,3 +1,18 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
declare module "*.vue" {
import Vue from "vue";
export default Vue;
diff --git a/src/index.ts b/src/index.ts
index 1afa74e..78a97c5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,7 @@
// along with this program. If not, see .
import { server as graphqlServer } from "@kredens/api";
+import { authMiddleware } from "@kredens/auth";
import { db } from "@kredens/db";
import logger from "@kredens/logger";
import indexRouter from "@kredens/routes/";
@@ -25,18 +26,33 @@ import pinoExpress from "express-pino-logger";
import session, { SessionOptions } from "express-session";
import helmet from "helmet";
import createHttpError from "http-errors";
-
async function main() {
await db.tx(async t => {
await t.migrations.create();
await t.migrations.apply();
});
- const server = graphqlServer();
const app = express();
const expressPino = pinoExpress({ logger });
app.use(helmet());
app.use(expressPino);
+
+ if (app.settings.env === "development") {
+ const webpack = await import("webpack").then(p => p.default); // tslint:disable-line:no-implicit-dependencies
+ // tslint:disable-next-line:no-implicit-dependencies
+ const webpackDevMiddleware = await import("webpack-dev-middleware").then(
+ p => p.default
+ );
+ const config = await import("../webpack.config").then(p => p.default);
+
+ const compiler = webpack(config);
+ app.use(
+ webpackDevMiddleware(compiler, {
+ publicPath: "/assets/"
+ })
+ );
+ }
+
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
@@ -52,25 +68,26 @@ async function main() {
}
app.use(session(sessionOptions));
app.use("/bootstrap", bootstrapRouter);
+
+ app.use(authMiddleware());
+ const apiServer = graphqlServer();
+ apiServer.applyMiddleware({
+ app,
+ path: "/graphql"
+ });
+
app.use(csrf());
- if (app.settings.env === "development") {
- const webpack = require("webpack"); // tslint:disable-line:no-implicit-dependencies
- const webpackDevMiddleware = require("webpack-dev-middleware"); // tslint:disable-line:no-implicit-dependencies
- const config = require("../webpack.config").default;
-
- const compiler = webpack(config);
- app.use(
- webpackDevMiddleware(compiler, {
- publicPath: "/assets/"
- })
- );
- }
-
app.set("view engine", "pug");
+ app.use(async (req, res, next) => {
+ res.locals.csrfToken = req.csrfToken();
+ res.locals.user = req.user;
+
+ next();
+ });
+
app.use("/", indexRouter);
- server.applyMiddleware({ app, path: "/graphql" });
app.use((req, res, next) => {
next(createHttpError(404));
@@ -79,7 +96,7 @@ async function main() {
const port = 3000;
app.listen(port, () =>
logger.info("Example app listening", {
- uri: `http://localhost:${port}${server.graphqlPath}`
+ uri: `http://localhost:${port}${apiServer.graphqlPath}`
})
);
}
diff --git a/src/routes/auth.ts b/src/routes/auth.ts
index 11f4529..3495419 100644
--- a/src/routes/auth.ts
+++ b/src/routes/auth.ts
@@ -19,17 +19,16 @@ import express from "express";
const router = express.Router();
router.get("/", async (req, res, next) => {
- res.render("login", {
- csrfToken: req.csrfToken()
- });
+ res.render("login");
});
router.post("/", async (req, res, next) => {
const userID = await db.users.login(req.body.email, req.body.password);
if (userID.isSome()) {
- res.send(`Hi, ${userID.some()}`);
+ req.session.userID = userID.some();
+ res.redirect("/");
} else {
- res.send(`Go away.`);
+ res.redirect("/auth/");
}
});
diff --git a/src/routes/bootstrap.ts b/src/routes/bootstrap.ts
index 6250f1d..96a1b07 100644
--- a/src/routes/bootstrap.ts
+++ b/src/routes/bootstrap.ts
@@ -1,3 +1,18 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
import { box, unbox } from "@kredens/crypto";
import { db } from "@kredens/db";
import express from "express";
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 25e6f8c..e858234 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -1,3 +1,18 @@
+// Copyright (C) 2019 ModZero
+//
+// 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 .
+
import express from "express";
import authRouter from "./auth";
import homeRouter from "./home";
diff --git a/tsconfig.json b/tsconfig.json
index e10e8b3..9e56bba 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,7 +11,7 @@
"*"
]
},
- "esModuleInterop": true,
+ "esModuleInterop": true
},
"include": [
"./src/**/*"
diff --git a/views/index.pug b/views/index.pug
index d695bcc..5a71acb 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -1,8 +1,15 @@
-html
- head
- title= title
- body
- h1= message
- div#body
- p I am a placeholder
- script(src="/assets/main.bundle.js")
\ No newline at end of file
+extends layout.pug
+
+block content
+ if user
+ p.auth Hi, #{ user.email }
+ else
+ p.auth
+ | Who are you?
+ |
+ a(href="/auth") Login you coward.
+ div#body
+ p I am a placeholder
+
+block scripts
+ script(src="/assets/main.bundle.js")
\ No newline at end of file
diff --git a/views/layout.pug b/views/layout.pug
index befcb35..407669f 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -3,3 +3,4 @@ html
title Kredens - #{title}
body
block content
+ block scripts
\ No newline at end of file