-
Notifications
You must be signed in to change notification settings - Fork 224
Expand file tree
/
Copy pathmiddleware.ts
More file actions
55 lines (48 loc) · 1.75 KB
/
middleware.ts
File metadata and controls
55 lines (48 loc) · 1.75 KB
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
import { NextRequest, NextResponse } from 'next/server'
/**
* Optional HTTP Basic Auth protection.
*
* Set SIFTLY_USERNAME and SIFTLY_PASSWORD in your .env to enable.
* Leave both unset (the default) for unrestricted local access.
*
* The bookmarklet endpoint is excluded so cross-origin imports from x.com
* continue to work regardless of auth configuration.
*/
export function middleware(request: NextRequest): NextResponse {
const username = process.env.SIFTLY_USERNAME?.trim()
const password = process.env.SIFTLY_PASSWORD?.trim()
// No credentials configured → pass through (default local behaviour)
if (!username || !password) return NextResponse.next()
// Let the bookmarklet endpoint through — it's called cross-origin from x.com
// and can't include Basic Auth credentials.
if (request.nextUrl.pathname === '/api/import/bookmarklet') {
return NextResponse.next()
}
const authHeader = request.headers.get('Authorization')
if (authHeader?.startsWith('Basic ')) {
try {
const decoded = atob(authHeader.slice(6))
const colonIdx = decoded.indexOf(':')
if (colonIdx !== -1) {
const user = decoded.slice(0, colonIdx)
const pass = decoded.slice(colonIdx + 1)
if (user === username && pass === password) {
return NextResponse.next()
}
}
} catch {
// malformed base64 → fall through to 401
}
}
return new NextResponse('Unauthorized', {
status: 401,
headers: { 'WWW-Authenticate': 'Basic realm="Siftly"' },
})
}
export const config = {
matcher: [
// Match everything except Next.js internals (_next/static, _next/image,
// _next/webpack-hmr dev HMR websocket, etc.) and static root files.
'/((?!_next/|favicon.ico|icon.svg).*)',
],
}