Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Commit c828b3a

Browse files
committed
Consolidate repeating types, augment express.Request with User model
Update User model update "any" types to more precise
1 parent 1409fd7 commit c828b3a

File tree

5 files changed

+63
-46
lines changed

5 files changed

+63
-46
lines changed

src/config/passport.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import _ from "lodash";
55

66
// import { User, UserType } from '../models/User';
77
import { User, UserDocument } from "../models/User";
8-
import { Request, Response, NextFunction } from "express";
8+
import {genericExpressMethod} from "express-request-with-user";
99

1010
const LocalStrategy = passportLocal.Strategy;
1111
const FacebookStrategy = passportFacebook.Strategy;
@@ -25,12 +25,12 @@ passport.deserializeUser((id, done) => {
2525
* Sign in using Email and Password.
2626
*/
2727
passport.use(new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
28-
User.findOne({ email: email.toLowerCase() }, (err, user: any) => {
28+
User.findOne({ email: email.toLowerCase() }, (err, user: UserDocument) => {
2929
if (err) { return done(err); }
3030
if (!user) {
3131
return done(undefined, false, { message: `Email ${email} not found.` });
3232
}
33-
user.comparePassword(password, (err: Error, isMatch: boolean) => {
33+
user.comparePassword(password, (err, isMatch) => {
3434
if (err) { return done(err); }
3535
if (isMatch) {
3636
return done(undefined, user);
@@ -66,15 +66,15 @@ passport.use(new FacebookStrategy({
6666
callbackURL: "/auth/facebook/callback",
6767
profileFields: ["name", "email", "link", "locale", "timezone"],
6868
passReqToCallback: true
69-
}, (req: any, accessToken, refreshToken, profile, done) => {
69+
}, (req, accessToken, refreshToken, profile, done) => {
7070
if (req.user) {
7171
User.findOne({ facebook: profile.id }, (err, existingUser) => {
7272
if (err) { return done(err); }
7373
if (existingUser) {
7474
req.flash("errors", { msg: "There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account." });
7575
done(err);
7676
} else {
77-
User.findById(req.user.id, (err, user: any) => {
77+
User.findById(req.user.id, (err, user) => {
7878
if (err) { return done(err); }
7979
user.facebook = profile.id;
8080
user.tokens.push({ kind: "facebook", accessToken });
@@ -100,7 +100,7 @@ passport.use(new FacebookStrategy({
100100
req.flash("errors", { msg: "There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings." });
101101
done(err);
102102
} else {
103-
const user: any = new User();
103+
const user: UserDocument = new User();
104104
user.email = profile._json.email;
105105
user.facebook = profile.id;
106106
user.tokens.push({ kind: "facebook", accessToken });
@@ -120,7 +120,7 @@ passport.use(new FacebookStrategy({
120120
/**
121121
* Login Required middleware.
122122
*/
123-
export const isAuthenticated = (req: Request, res: Response, next: NextFunction) => {
123+
export const isAuthenticated: genericExpressMethod = (req, res, next) => {
124124
if (req.isAuthenticated()) {
125125
return next();
126126
}
@@ -130,11 +130,11 @@ export const isAuthenticated = (req: Request, res: Response, next: NextFunction)
130130
/**
131131
* Authorization Required middleware.
132132
*/
133-
export const isAuthorized = (req: Request, res: Response, next: NextFunction) => {
133+
export const isAuthorized: genericExpressMethod = (req, res, next) => {
134134
const provider = req.path.split("/").slice(-1)[0];
135135

136-
const user = req.user as UserDocument;
137-
if (_.find(user.tokens, { kind: provider })) {
136+
// const user = req.user as UserDocument;
137+
if (_.find(req.user.tokens, { kind: provider })) {
138138
next();
139139
} else {
140140
res.redirect(`/auth/${provider}`);

src/controllers/api.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
"use strict";
2-
31
import graph from "fbgraph";
4-
import { Response, Request, NextFunction } from "express";
5-
import { UserDocument } from "../models/User";
2+
import { Response, Request } from "express";
3+
import {genericExpressMethod} from "express-request-with-user";
64

75

86
/**
@@ -19,11 +17,11 @@ export const getApi = (req: Request, res: Response) => {
1917
* GET /api/facebook
2018
* Facebook API example.
2119
*/
22-
export const getFacebook = (req: Request, res: Response, next: NextFunction) => {
23-
const user = req.user as UserDocument;
24-
const token = user.tokens.find((token: any) => token.kind === "facebook");
20+
export const getFacebook: genericExpressMethod = (req, res, next) => {
21+
// const user = req.user /*as UserDocument*/;
22+
const token = req.user.tokens.find(token => token.kind === "facebook");
2523
graph.setAccessToken(token.accessToken);
26-
graph.get(`${user.facebook}?fields=id,name,email,first_name,last_name,gender,link,locale,timezone`, (err: Error, results: graph.FacebookUser) => {
24+
graph.get(`${req.user.facebook}?fields=id,name,email,first_name,last_name,gender,link,locale,timezone`, (err: Error, results: graph.FacebookUser) => {
2725
if (err) { return next(err); }
2826
res.render("api/facebook", {
2927
title: "Facebook API",

src/controllers/user.ts

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import async from "async";
22
import crypto from "crypto";
33
import nodemailer from "nodemailer";
44
import passport from "passport";
5-
import { User, UserDocument, AuthToken } from "../models/User";
6-
import { Request, Response, NextFunction } from "express";
7-
import { IVerifyOptions } from "passport-local";
8-
import { WriteError } from "mongodb";
9-
import { check, sanitize, validationResult } from "express-validator";
5+
import {AuthToken, User, UserDocument} from "../models/User";
6+
import {Request, Response} from "express";
7+
import {IVerifyOptions} from "passport-local";
8+
import {WriteError} from "mongodb";
9+
import {check, sanitize, validationResult} from "express-validator";
1010
import "../config/passport";
11+
import {genericExpressMethod} from "express-request-with-user";
1112

1213
/**
1314
* GET /login
@@ -26,7 +27,8 @@ export const getLogin = (req: Request, res: Response) => {
2627
* POST /login
2728
* Sign in using email and password.
2829
*/
29-
export const postLogin = (req: Request, res: Response, next: NextFunction) => {
30+
31+
export const postLogin: genericExpressMethod = (req, res, next) => {
3032
check("email", "Email is not valid").isEmail();
3133
check("password", "Password cannot be blank").isLength({min: 1});
3234
// eslint-disable-next-line @typescript-eslint/camelcase
@@ -79,7 +81,7 @@ export const getSignup = (req: Request, res: Response) => {
7981
* POST /signup
8082
* Create a new local account.
8183
*/
82-
export const postSignup = (req: Request, res: Response, next: NextFunction) => {
84+
export const postSignup: genericExpressMethod = (req, res, next) => {
8385
check("email", "Email is not valid").isEmail();
8486
check("password", "Password must be at least 4 characters long").isLength({ min: 4 });
8587
check("confirmPassword", "Passwords do not match").equals(req.body.password);
@@ -130,7 +132,7 @@ export const getAccount = (req: Request, res: Response) => {
130132
* POST /account/profile
131133
* Update profile information.
132134
*/
133-
export const postUpdateProfile = (req: Request, res: Response, next: NextFunction) => {
135+
export const postUpdateProfile: genericExpressMethod = (req, res, next) => {
134136
check("email", "Please enter a valid email address.").isEmail();
135137
// eslint-disable-next-line @typescript-eslint/camelcase
136138
sanitize("email").normalizeEmail({ gmail_remove_dots: false });
@@ -142,8 +144,8 @@ export const postUpdateProfile = (req: Request, res: Response, next: NextFunctio
142144
return res.redirect("/account");
143145
}
144146

145-
const user = req.user as UserDocument;
146-
User.findById(user.id, (err, user: UserDocument) => {
147+
// const user = req.user as UserDocument;
148+
User.findById(req.user.id, (err, user) => {
147149
if (err) { return next(err); }
148150
user.email = req.body.email || "";
149151
user.profile.name = req.body.name || "";
@@ -168,7 +170,7 @@ export const postUpdateProfile = (req: Request, res: Response, next: NextFunctio
168170
* POST /account/password
169171
* Update current password.
170172
*/
171-
export const postUpdatePassword = (req: Request, res: Response, next: NextFunction) => {
173+
export const postUpdatePassword: genericExpressMethod = (req, res, next) => {
172174
check("password", "Password must be at least 4 characters long").isLength({ min: 4 });
173175
check("confirmPassword", "Passwords do not match").equals(req.body.password);
174176

@@ -179,8 +181,8 @@ export const postUpdatePassword = (req: Request, res: Response, next: NextFuncti
179181
return res.redirect("/account");
180182
}
181183

182-
const user = req.user as UserDocument;
183-
User.findById(user.id, (err, user: UserDocument) => {
184+
// const user = req.user as UserDocument;
185+
User.findById(req.user.id, (err, user) => {
184186
if (err) { return next(err); }
185187
user.password = req.body.password;
186188
user.save((err: WriteError) => {
@@ -195,9 +197,9 @@ export const postUpdatePassword = (req: Request, res: Response, next: NextFuncti
195197
* POST /account/delete
196198
* Delete user account.
197199
*/
198-
export const postDeleteAccount = (req: Request, res: Response, next: NextFunction) => {
199-
const user = req.user as UserDocument;
200-
User.remove({ _id: user.id }, (err) => {
200+
export const postDeleteAccount: genericExpressMethod = (req, res, next) => {
201+
// const user = req.user as UserDocument;
202+
User.remove({ _id: req.user.id }, (err) => {
201203
if (err) { return next(err); }
202204
req.logout();
203205
req.flash("info", { msg: "Your account has been deleted." });
@@ -209,12 +211,12 @@ export const postDeleteAccount = (req: Request, res: Response, next: NextFunctio
209211
* GET /account/unlink/:provider
210212
* Unlink OAuth provider.
211213
*/
212-
export const getOauthUnlink = (req: Request, res: Response, next: NextFunction) => {
214+
export const getOauthUnlink: genericExpressMethod = (req, res, next) => {
213215
const provider = req.params.provider;
214-
const user = req.user as UserDocument;
215-
User.findById(user.id, (err, user: any) => {
216+
// const user = req.user as UserDocument;
217+
User.findById(req.user.id, (err, user) => {
216218
if (err) { return next(err); }
217-
user[provider] = undefined;
219+
// user[provider] = undefined;
218220
user.tokens = user.tokens.filter((token: AuthToken) => token.kind !== provider);
219221
user.save((err: WriteError) => {
220222
if (err) { return next(err); }
@@ -228,7 +230,7 @@ export const getOauthUnlink = (req: Request, res: Response, next: NextFunction)
228230
* GET /reset/:token
229231
* Reset Password page.
230232
*/
231-
export const getReset = (req: Request, res: Response, next: NextFunction) => {
233+
export const getReset: genericExpressMethod = (req, res, next) => {
232234
if (req.isAuthenticated()) {
233235
return res.redirect("/");
234236
}
@@ -251,7 +253,7 @@ export const getReset = (req: Request, res: Response, next: NextFunction) => {
251253
* POST /reset/:token
252254
* Process the reset password request.
253255
*/
254-
export const postReset = (req: Request, res: Response, next: NextFunction) => {
256+
export const postReset: genericExpressMethod = (req, res, next) => {
255257
check("password", "Password must be at least 4 characters long.").isLength({ min: 4 });
256258
check("confirm", "Passwords must match.").equals(req.body.password);
257259

@@ -267,7 +269,7 @@ export const postReset = (req: Request, res: Response, next: NextFunction) => {
267269
User
268270
.findOne({ passwordResetToken: req.params.token })
269271
.where("passwordResetExpires").gt(Date.now())
270-
.exec((err, user: any) => {
272+
.exec((err, user) => {
271273
if (err) { return next(err); }
272274
if (!user) {
273275
req.flash("errors", { msg: "Password reset token is invalid or has expired." });
@@ -326,7 +328,7 @@ export const getForgot = (req: Request, res: Response) => {
326328
* POST /forgot
327329
* Create a random token, then the send user an email with a reset link.
328330
*/
329-
export const postForgot = (req: Request, res: Response, next: NextFunction) => {
331+
export const postForgot: genericExpressMethod = (req, res, next) => {
330332
check("email", "Please enter a valid email address.").isEmail();
331333
// eslint-disable-next-line @typescript-eslint/camelcase
332334
sanitize("email").normalizeEmail({ gmail_remove_dots: false });
@@ -346,14 +348,14 @@ export const postForgot = (req: Request, res: Response, next: NextFunction) => {
346348
});
347349
},
348350
function setRandomToken(token: AuthToken, done: Function) {
349-
User.findOne({ email: req.body.email }, (err, user: any) => {
351+
User.findOne({ email: req.body.email }, (err, user) => {
350352
if (err) { return done(err); }
351353
if (!user) {
352354
req.flash("errors", { msg: "Account with that email address does not exist." });
353355
return res.redirect("/forgot");
354356
}
355357
user.passwordResetToken = token;
356-
user.passwordResetExpires = Date.now() + 3600000; // 1 hour
358+
user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 hour
357359
user.save((err: WriteError) => {
358360
done(err, token, user);
359361
});

src/models/User.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import mongoose from "mongoose";
55
export type UserDocument = mongoose.Document & {
66
email: string;
77
password: string;
8-
passwordResetToken: string;
8+
passwordResetToken: AuthToken;
9+
// passwordResetToken: string;
910
passwordResetExpires: Date;
1011

1112
facebook: string;
@@ -21,9 +22,11 @@ export type UserDocument = mongoose.Document & {
2122

2223
comparePassword: comparePasswordFunction;
2324
gravatar: (size: number) => string;
25+
26+
// [provider: string]: any;
2427
};
2528

26-
type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => {}) => void;
29+
type comparePasswordFunction = (candidatePassword: string, cb: (err: Error, isMatch: boolean) => void) => void;
2730

2831
export interface AuthToken {
2932
accessToken: string;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference types="express" />
2+
3+
import {UserDocument} from "../models/User";
4+
import {NextFunction, Request, Response} from "express";
5+
6+
declare module 'express' {
7+
export interface User extends UserDocument {}
8+
export interface Request {
9+
user?: User;
10+
}
11+
}
12+
13+
/** To avoid duplication */
14+
export type genericExpressMethod = (req: Request, res: Response, next: NextFunction) => void

0 commit comments

Comments
 (0)