-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathshared-middleware.ts
143 lines (129 loc) · 3.71 KB
/
shared-middleware.ts
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
136
137
138
139
140
141
142
143
import Joi from 'joi'
import { ServerConfig } from 'lib/server/config'
import { RadioRepository } from 'lib/server/radio-repository'
import { schemas } from 'lib/server/schemas'
import {
PublicServerError,
ServerError,
ValidationError
} from 'lib/server/server-error'
import {
countryDataByKey,
fetchIpInfo,
getSongInfo,
logServerError
} from 'lib/server/utils'
import { SharedConfig } from 'lib/shared/config'
import { Session } from 'next-auth'
import { getSession } from 'next-auth/react'
import { Koa } from 'nextjs-koa-api'
import { PumpIt } from 'pumpit'
import { RadioBrowserApi } from 'radio-browser-api'
import requestIp from 'request-ip'
export interface ApiContext extends Koa.DefaultContext {
deps: {
config: ServerConfig
sharedConfig: SharedConfig
getSession: typeof getSession
radioRepository: RadioRepository
radioApi: RadioBrowserApi
logServerError: ReturnType<typeof logServerError>
getSongInfo: typeof getSongInfo
schemas: typeof schemas
requestIp: typeof requestIp
countryDataByKey: typeof countryDataByKey
fetchIpInfo: typeof fetchIpInfo
}
}
export interface ApiState extends Koa.DefaultState {
session?: Session
}
/**
* Builds Koa context
* @param container - injection container with context dependencies
*/
export function buildContext(container: PumpIt) {
return async (
ctx: Koa.ParameterizedContext<Koa.DefaultState, ApiContext>,
next: Koa.Next
) => {
ctx.deps = {
config: container.resolve<ServerConfig>('config'),
sharedConfig: container.resolve<SharedConfig>('sharedConfig'),
radioRepository: container.resolve<RadioRepository>(RadioRepository),
getSession: container.resolve<typeof getSession>(getSession),
radioApi: container.resolve<RadioBrowserApi>(RadioBrowserApi),
logServerError:
container.resolve<ReturnType<typeof logServerError>>(logServerError),
schemas: container.resolve<typeof schemas>(schemas),
getSongInfo: container.resolve<typeof getSongInfo>(getSongInfo),
requestIp: container.resolve<typeof requestIp>(requestIp),
fetchIpInfo: container.resolve<typeof fetchIpInfo>(fetchIpInfo),
countryDataByKey:
container.resolve<typeof countryDataByKey>(countryDataByKey)
}
return next()
}
}
/**
* Check if user is authenticated
*/
export async function checkSession(
ctx: Koa.ParameterizedContext<ApiState, ApiContext>,
next: Koa.Next
) {
const { getSession } = ctx.deps
const session = await getSession({ req: ctx.req })
if (!session) {
throw new PublicServerError({
body: { msg: 'not authenticated' },
status: 401
})
}
ctx.state.session = session
return next()
}
export async function handleServerError(
ctx: Koa.ParameterizedContext<Koa.DefaultState, ApiContext>,
next: Koa.Next
) {
try {
await next()
} catch (err: any) {
const { logServerError, config } = ctx.deps
let status = 500
let exposeError = false
let logIt = true
let payload: Record<string, any> | undefined = {
msg: 'internal server error'
}
let validationError: Joi.ValidationError | undefined
if (err instanceof ServerError) {
status = err.status
logIt = err.logIt
exposeError = err.expose
if (exposeError && err.body) {
payload = err.body
}
if (err instanceof ValidationError) {
validationError = err.validationError
}
}
if (config.isDevelopment) {
payload = {
...payload,
stack: err.stack,
message: err.message,
validationError,
dev: err.dev
}
}
if (!ctx.headerSent) {
ctx.status = status
ctx.body = payload
}
if (logIt) {
logServerError(err, ctx)
}
}
}