-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
135 lines (117 loc) · 3.76 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const Url = require('url-parse');
const isSANB = require('is-string-and-not-blank');
class RedirectLoop {
constructor(config) {
this.config = {
getDefaultPath: (ctx) =>
ctx.request.locale ? `/${ctx.request.locale}` : '/',
maxRedirects: 5,
logger: console,
...config
};
if (isSANB(this.config.defaultPath))
this.config.getDefaultPath = () => this.config.defaultPath;
if (
typeof this.config.maxRedirects !== 'number' ||
this.config.maxRedirects <= 0
)
throw new Error('maxRedirects must be a Number greater than zero');
this.middleware = this.middleware.bind(this);
}
async middleware(ctx, next) {
const { config } = this;
const { redirect } = ctx;
if (!ctx.session) {
config.logger.error(
new Error(
'ctx.session missing, sessions required for koa-redirect-loop'
)
);
return next();
}
if (typeof ctx.saveSession !== 'function') {
config.logger.error(
new Error(
'Please use koa-generic-session v2.0.3+ which exposes a `ctx.saveSession()` method'
)
);
return next();
}
ctx.redirect = function (url, alt) {
let address = url;
const defaultPath = config.getDefaultPath(ctx);
if (url === 'back') {
//
// NOTE: we can only use the Referrer if they're from the same site
//
address =
ctx.get('Referrer') &&
new Url(ctx.get('Referrer'), {}).origin ===
new Url(ctx.href, {}).origin
? new Url(ctx.get('Referrer'), {}).pathname || defaultPath
: alt || defaultPath;
}
const previousPreviousPath = ctx.session.prevPrevPath;
const previousPath = ctx.session.prevPath || defaultPath;
const previousMethod = ctx.session.prevMethod || ctx.method;
const maxRedirects = ctx.session.maxRedirects || 1;
if (
previousPath &&
address === previousPath &&
ctx.method === previousMethod &&
previousPreviousPath
) {
if (
address !== previousPreviousPath &&
maxRedirects <= config.maxRedirects
) {
address = previousPreviousPath;
} else {
// if the prevPrevPath w/o querystring is !== prevPrevPath
// then redirect then to prevPrevPath w/o querystring
const { pathname } = new Url(previousPreviousPath, {});
address =
pathname === previousPreviousPath
? defaultPath
: pathname || defaultPath;
}
} else if (maxRedirects > config.maxRedirects) {
address = defaultPath;
}
redirect.call(this, address, alt);
};
let error;
try {
await next();
} catch (err) {
error = err;
}
//
// instead of `!req.xhr` we need to use !accepts HTML
// because Fetch does not provide XMLHttpRequest
//
if (ctx.accepts('html')) {
// if it was successful then unset prevPrevPath
if (ctx.res.statusCode === 200) delete ctx.session.prevPrevPath;
else ctx.session.prevPrevPath = ctx.session.prevPath;
ctx.session.prevPath = ctx.originalUrl;
ctx.session.prevMethod = ctx.method;
// if it was a redirect then store how many times
// so that we can limit the max number of redirects
if ([301, 302].includes(ctx.res.statusCode))
ctx.session.maxRedirects =
typeof ctx.session.maxRedirects === 'number'
? ctx.session.maxRedirects + 1
: 1;
else ctx.session.maxRedirects = 0;
}
try {
await ctx.saveSession();
} catch (err) {
// this indicates an issue with redis most likely
config.logger.error(err);
}
if (error) throw error;
}
}
module.exports = RedirectLoop;