updated codegen, fixed tsconfig, added prisma
added login, registration and session check
This commit is contained in:
parent
9518787206
commit
04b606b7fa
24 changed files with 740 additions and 1747 deletions
|
|
@ -1,15 +1,20 @@
|
|||
/* gql imports */
|
||||
import { QueryResolvers } from "@generated/graphql";
|
||||
import { QueryResolvers } from '@generated/types';
|
||||
|
||||
/* Query */
|
||||
import { BooksManyResolver } from "./resolvers/booksManyResolver";
|
||||
import { BookOneResolver } from "./resolvers/bookOneResolver";
|
||||
import { BooksManyResolver } from './resolvers/booksManyResolver';
|
||||
import { BookOneResolver } from './resolvers/bookOneResolver';
|
||||
|
||||
/* Mutation */
|
||||
import { LoginResolver } from './resolvers/auth/login';
|
||||
|
||||
export const Query: QueryResolvers = {
|
||||
booksMany: BooksManyResolver,
|
||||
bookOne: BookOneResolver,
|
||||
bookOne: BookOneResolver
|
||||
};
|
||||
|
||||
export const Mutation = {};
|
||||
export const Mutation = {
|
||||
login: LoginResolver
|
||||
};
|
||||
|
||||
export const Subscription = {};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,44 @@
|
|||
import { prisma } from '@app/server/prisma/client';
|
||||
import { MutationLoginArgs, Session } from '@generated/types';
|
||||
import bcrypt from 'bcrypt';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const usersDB = {
|
||||
users: require('../model/users.json'),
|
||||
setUsers: function (data) {
|
||||
this.users = data;
|
||||
}
|
||||
};
|
||||
export async function LoginResolver(_: any, { input }: MutationLoginArgs): Promise<Session> {
|
||||
const error = 'Invalid username or password';
|
||||
|
||||
const handleLogin = async (req, res) => {
|
||||
const { user, pwd } = req.body;
|
||||
if (!user || !pwd) res.status(400).json({ message: 'Username and password are required.' });
|
||||
const foundUser = usersDB.users.find(person => person.username === user);
|
||||
if (!foundUser) return res.sendStatus(401); // unauthorized
|
||||
// evaluate password
|
||||
const match = await bcrypt.compare(pwd, foundUser.password);
|
||||
if (match) {
|
||||
// create JWTs
|
||||
res.json({ success: `User ${user} is logged in.` });
|
||||
} else {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
};
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: input.email }
|
||||
});
|
||||
if (!user) throw new Error(error);
|
||||
|
||||
module.exports = { handleLogin };
|
||||
const match = await bcrypt.compare(input.password, user.password);
|
||||
if (!match) throw new Error(error);
|
||||
|
||||
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET;
|
||||
const refreshTokenSecret = process.env.REFRESH_TOKEN_SECRET;
|
||||
|
||||
if (accessTokenSecret == null || refreshTokenSecret == null) throw new Error('No secrets found');
|
||||
|
||||
const accessToken = jwt.sign({ userId: user.id }, accessTokenSecret, {
|
||||
expiresIn: '30s'
|
||||
});
|
||||
const refreshToken = jwt.sign({ userId: user.id }, refreshTokenSecret, {
|
||||
expiresIn: '1d'
|
||||
});
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { refreshToken }
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: {
|
||||
token: refreshToken,
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
maxAge: 24 * 60 * 60 * 1000,
|
||||
sameSite: 'None'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,28 @@
|
|||
import { prisma, uuid } from '@app/server/prisma/client';
|
||||
import { RegisterInput } from '@generated/types';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
export async function SignUpResolver(_: any, input: any): Promise<any> {
|
||||
// validate
|
||||
export async function SignUpResolver(_: any, input: RegisterInput): Promise<void> {
|
||||
const existingUsername = await prisma.user.findUnique({
|
||||
where: { username: input.username },
|
||||
select: { id: true }
|
||||
});
|
||||
if (existingUsername != null) throw Error('User with this username already exists.');
|
||||
|
||||
// db
|
||||
const users = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'joe@test.com',
|
||||
username: 'Joe',
|
||||
password: 'test'
|
||||
const existingEmail = await prisma.user.findUnique({
|
||||
where: { email: input.email },
|
||||
select: { id: true }
|
||||
});
|
||||
if (existingEmail != null) throw Error('Email is already in use.');
|
||||
|
||||
const hashedPassword = await bcrypt.hash(input.password, 10);
|
||||
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
id: uuid(),
|
||||
email: input.email,
|
||||
username: input.username,
|
||||
password: hashedPassword
|
||||
}
|
||||
];
|
||||
const usersDB = { users };
|
||||
|
||||
// check for duplicate email+username
|
||||
|
||||
// encrypt the password
|
||||
const hashedPwd = await bcrypt.hash(input.pwd, 10);
|
||||
|
||||
// store user in the db, return new user
|
||||
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
const handleNewUser = async (req, res) => {
|
||||
const { user, pwd } = req.body;
|
||||
if (!user || !pwd)
|
||||
return res.status(400).json({ message: 'Username and password are required.' });
|
||||
// check for duplicate usernames in the db.
|
||||
const duplicate = usersDB.users.find(person => person.username === user);
|
||||
if (duplicate) return res.status(409);
|
||||
try {
|
||||
// encrypt the password
|
||||
const hashedPwd = await brcypt.hash(pwd, 10);
|
||||
// store the new user
|
||||
const newUser = { username: user, password: hashedPwd };
|
||||
usersDB.setUsers([...usersDB.users, newUser]);
|
||||
await fs_promises.writeFile(
|
||||
path.join(__dirname, '..', 'model', 'users.json'),
|
||||
JSON.stringify(usersDB.users)
|
||||
);
|
||||
console.log(usersDB.users);
|
||||
res.status(201).json({ success: `New user ${user} created!` });
|
||||
} catch (err) {
|
||||
res.status(500).json({ message: err.message });
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { BookInput, Book } from "@generated/graphql";
|
||||
import { books } from "./booksManyResolver";
|
||||
import { Book, QueryBookOneArgs } from '@generated/types';
|
||||
import { books } from './booksManyResolver';
|
||||
|
||||
export async function BookOneResolver(_: any, input: BookInput): Promise<Book> {
|
||||
console.log("Called 'BookOneResolver' resolver...");
|
||||
|
||||
const book = books.filter((item) => item.id === input.id)[0];
|
||||
export async function BookOneResolver(_: any, { id }: QueryBookOneArgs): Promise<Book> {
|
||||
const book = books.filter(item => item.id === id)[0];
|
||||
|
||||
return book;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
import { Book } from "@generated/graphql";
|
||||
import { Book } from '@generated/types';
|
||||
|
||||
export const books = [
|
||||
{
|
||||
id: "1",
|
||||
title: "some-title-1",
|
||||
author: "someone",
|
||||
id: '1',
|
||||
title: 'some-title-1',
|
||||
author: 'someone'
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "some-title-1",
|
||||
author: "someone",
|
||||
},
|
||||
id: '2',
|
||||
title: 'some-title-1',
|
||||
author: 'someone'
|
||||
}
|
||||
];
|
||||
|
||||
export async function BooksManyResolver(_: any): Promise<Book[]> {
|
||||
console.log("Called 'BooksManyResolver' resolver...");
|
||||
|
||||
return books;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,37 @@
|
|||
scalar Void
|
||||
|
||||
type Query {
|
||||
booksMany: [Book]!
|
||||
bookOne(input: BookInput!): [Book]!
|
||||
booksMany: [Book!]!
|
||||
bookOne(id: ID!): Book!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
login(input: LoginInput!): Session!
|
||||
register(input: RegisterInput!): Void!
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
email: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
input RegisterInput {
|
||||
email: String!
|
||||
username: String!
|
||||
password: String!
|
||||
}
|
||||
|
||||
type Session {
|
||||
accessToken: String!
|
||||
refreshToken: RefreshToken!
|
||||
}
|
||||
|
||||
type RefreshToken {
|
||||
token: String!
|
||||
maxAge: Int!
|
||||
secure: Boolean!
|
||||
httpOnly: Boolean!
|
||||
sameSite: String!
|
||||
}
|
||||
|
||||
type Book {
|
||||
|
|
@ -8,7 +39,3 @@ type Book {
|
|||
title: String!
|
||||
author: String!
|
||||
}
|
||||
|
||||
input BookInput {
|
||||
id: ID!
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue