adapt-authoring-server 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,191 +1,191 @@
1
- import _ from 'lodash'
2
- import { App } from 'adapt-authoring-core'
3
- /**
4
- * Server-related utilities
5
- * @memberof server
6
- */
7
- class ServerUtils {
8
- /**
9
- * Middleware for handling 404 errors on the API router
10
- * @param {external:ExpressRequest} req
11
- * @param {external:ExpressResponse} res
12
- * @param {Function} next
13
- */
14
- static apiNotFoundHandler (req, res, next) {
15
- next(App.instance.errors.ENDPOINT_NOT_FOUND.setData({ endpoint: req.originalUrl, method: req.method }))
16
- }
17
-
18
- /**
19
- * Generic error handling middleware for the API router
20
- * @param {Error} error
21
- * @param {external:ExpressRequest} req
22
- * @param {external:ExpressResponse} res
23
- * @param {Function} next
24
- */
25
- static genericErrorHandler (error, req, res, next) {
26
- this.log('error', App.instance.lang.translate(undefined, error), this.getConfig('verboseErrorLogging') && error.stack ? error.stack : '')
27
- res.sendError(error)
28
- }
29
-
30
- /**
31
- * Middleware for handling 404 errors on the root router
32
- * @param {external:ExpressRequest} req
33
- * @param {external:ExpressResponse} res
34
- */
35
- static rootNotFoundHandler (req, res) {
36
- res.status(App.instance.errors.NOT_FOUND.statusCode).end()
37
- }
38
-
39
- /**
40
- * Handler for returning an API map
41
- * @param {Router} topRouter
42
- * @return {Function} Middleware function
43
- */
44
- static mapHandler (topRouter) {
45
- return (req, res) => res.json(topRouter.map)
46
- }
47
-
48
- /**
49
- * Generates a map for a given router
50
- * @param {Router} topRouter
51
- * @return {Object} The route map
52
- */
53
- static generateRouterMap (topRouter) {
54
- return topRouter.flattenRouters()
55
- .sort((a, b) => a.root.localeCompare(b.root))
56
- .reduce((m, r) => {
57
- const key = `${getRelativeRoute(topRouter, r)}endpoints`
58
- const endpoints = getEndpoints(r)
59
- return endpoints.length ? { ...m, [key]: endpoints } : m
60
- }, {})
61
- }
62
-
63
- /**
64
- * Adds extra properties to the request object to allow for easy translations
65
- * @param {Function} next
66
- */
67
- static addErrorHandler (req, res, next) {
68
- res.sendError = error => {
69
- if (error.constructor.name !== 'AdaptError') {
70
- const e = App.instance.errors[error.code]
71
- if (e) {
72
- if (error.statusCode) e.statusCode = error.statusCode
73
- e.error = error.message
74
- error = e
75
- } else {
76
- error = App.instance.errors.SERVER_ERROR
77
- }
78
- }
79
- res
80
- .status(error.statusCode)
81
- .json({ code: error.code, message: req.translate?.(error) ?? error.message })
82
- }
83
- next()
84
- }
85
-
86
- /**
87
- * Adds logs for debugging each request time
88
- * @param {external:ExpressRequest} req
89
- * @param {external:ExpressResponse} res
90
- * @param {Function} next
91
- */
92
- static async debugRequestTime (req, res, next) {
93
- const server = await App.instance.waitForModule('server')
94
- if (server.getConfig('debugRequestTime')) {
95
- const start = new Date()
96
- res.on('finish', () => server.log('debug', 'REQUEST_DURATION', req.method, req.originalUrl, new Date() - start))
97
- }
98
- next()
99
- }
100
-
101
- /**
102
- * Adds extra properties to the request object to allow for easy existence checking of common request objects
103
- * @param {external:ExpressRequest} req
104
- * @param {external:ExpressResponse} res
105
- * @param {Function} next
106
- * @example
107
- * "IMPORTANT NOTE: body data is completely ignored for GET requests, any code
108
- * requiring it should switch to use POST."
109
- *
110
- * let req = { 'params': { 'foo':'bar' }, 'query': {}, 'body': {} };
111
- * req.hasParams // true
112
- * req.hasQuery // false
113
- * req.hasBody // false
114
- */
115
- static addExistenceProps (req, res, next) {
116
- if (req.method === 'GET') {
117
- req.body = {}
118
- }
119
- const storeVal = (key, exists) => {
120
- req[`has${_.capitalize(key)}`] = exists
121
- }
122
- ['body', 'params', 'query'].forEach(attr => {
123
- if (!req[attr]) {
124
- return storeVal(attr, true)
125
- }
126
- const entries = Object.entries(req[attr])
127
- let deleted = 0
128
- if (entries.length === 0) {
129
- return storeVal(attr, false)
130
- }
131
- entries.forEach(([key, val]) => {
132
- if (val === undefined || val === null) {
133
- delete req[attr][key]
134
- deleted++
135
- }
136
- })
137
- storeVal(attr, deleted < entries.length)
138
- })
139
- next()
140
- }
141
-
142
- /**
143
- * Handles restriction of routes marked as internal
144
- * @param {external:ExpressRequest} req
145
- * @param {external:ExpressResponse} res
146
- * @param {Function} next
147
- */
148
- static async handleInternalRoutes (req, res, next) {
149
- const server = await App.instance.waitForModule('server')
150
- const isInternalIp = server.getConfig('host') === req.ip || req.ip === '127.0.0.1' || req.ip === '::1'
151
- if (req.routeConfig.internal && !isInternalIp) {
152
- return next(App.instance.errors.UNAUTHORISED.setData({ url: req.originalUrl, method: req.method }))
153
- }
154
- next()
155
- }
156
-
157
- /**
158
- * Caches the route config on the incoming request
159
- * @param {Route} routeConfig
160
- * @return {Function}
161
- */
162
- static cacheRouteConfig (routeConfig) {
163
- return (req, res, next) => {
164
- req.routeConfig = routeConfig
165
- next()
166
- }
167
- }
168
- }
169
- /** @ignore */ function getEndpoints (r) {
170
- return r.routes.map(route => {
171
- return {
172
- url: `${r.url}${route.route}`,
173
- accepted_methods: Object.keys(route.handlers).reduce((memo, method) => {
174
- return {
175
- ...memo,
176
- [method]: route?.meta?.[method] ?? {}
177
- }
178
- }, {})
179
- }
180
- })
181
- }
182
- /** @ignore */ function getRelativeRoute (relFrom, relTo) {
183
- if (relFrom === relTo) {
184
- return `${relFrom.route}_`
185
- }
186
- let route = ''
187
- for (let r = relTo; r !== relFrom; r = r.parentRouter) route = `${r.root}_${route}`
188
- return route
189
- }
190
-
191
- export default ServerUtils
1
+ import _ from 'lodash'
2
+ import { App } from 'adapt-authoring-core'
3
+ /**
4
+ * Server-related utilities
5
+ * @memberof server
6
+ */
7
+ class ServerUtils {
8
+ /**
9
+ * Middleware for handling 404 errors on the API router
10
+ * @param {external:ExpressRequest} req
11
+ * @param {external:ExpressResponse} res
12
+ * @param {Function} next
13
+ */
14
+ static apiNotFoundHandler (req, res, next) {
15
+ next(App.instance.errors.ENDPOINT_NOT_FOUND.setData({ endpoint: req.originalUrl, method: req.method }))
16
+ }
17
+
18
+ /**
19
+ * Generic error handling middleware for the API router
20
+ * @param {Error} error
21
+ * @param {external:ExpressRequest} req
22
+ * @param {external:ExpressResponse} res
23
+ * @param {Function} next
24
+ */
25
+ static genericErrorHandler (error, req, res, next) {
26
+ this.log('error', App.instance.lang.translate(undefined, error), this.getConfig('verboseErrorLogging') && error.stack ? error.stack : '')
27
+ res.sendError(error)
28
+ }
29
+
30
+ /**
31
+ * Middleware for handling 404 errors on the root router
32
+ * @param {external:ExpressRequest} req
33
+ * @param {external:ExpressResponse} res
34
+ */
35
+ static rootNotFoundHandler (req, res) {
36
+ res.status(App.instance.errors.NOT_FOUND.statusCode).end()
37
+ }
38
+
39
+ /**
40
+ * Handler for returning an API map
41
+ * @param {Router} topRouter
42
+ * @return {Function} Middleware function
43
+ */
44
+ static mapHandler (topRouter) {
45
+ return (req, res) => res.json(topRouter.map)
46
+ }
47
+
48
+ /**
49
+ * Generates a map for a given router
50
+ * @param {Router} topRouter
51
+ * @return {Object} The route map
52
+ */
53
+ static generateRouterMap (topRouter) {
54
+ return topRouter.flattenRouters()
55
+ .sort((a, b) => a.root.localeCompare(b.root))
56
+ .reduce((m, r) => {
57
+ const key = `${getRelativeRoute(topRouter, r)}endpoints`
58
+ const endpoints = getEndpoints(r)
59
+ return endpoints.length ? { ...m, [key]: endpoints } : m
60
+ }, {})
61
+ }
62
+
63
+ /**
64
+ * Adds extra properties to the request object to allow for easy translations
65
+ * @param {Function} next
66
+ */
67
+ static addErrorHandler (req, res, next) {
68
+ res.sendError = error => {
69
+ if (error.constructor.name !== 'AdaptError') {
70
+ const e = App.instance.errors[error.code]
71
+ if (e) {
72
+ if (error.statusCode) e.statusCode = error.statusCode
73
+ e.error = error.message
74
+ error = e
75
+ } else {
76
+ error = App.instance.errors.SERVER_ERROR
77
+ }
78
+ }
79
+ res
80
+ .status(error.statusCode)
81
+ .json({ code: error.code, message: req.translate?.(error) ?? error.message })
82
+ }
83
+ next()
84
+ }
85
+
86
+ /**
87
+ * Adds logs for debugging each request time
88
+ * @param {external:ExpressRequest} req
89
+ * @param {external:ExpressResponse} res
90
+ * @param {Function} next
91
+ */
92
+ static async debugRequestTime (req, res, next) {
93
+ const server = await App.instance.waitForModule('server')
94
+ if (server.getConfig('debugRequestTime')) {
95
+ const start = new Date()
96
+ res.on('finish', () => server.log('debug', 'REQUEST_DURATION', req.method, req.originalUrl, new Date() - start))
97
+ }
98
+ next()
99
+ }
100
+
101
+ /**
102
+ * Adds extra properties to the request object to allow for easy existence checking of common request objects
103
+ * @param {external:ExpressRequest} req
104
+ * @param {external:ExpressResponse} res
105
+ * @param {Function} next
106
+ * @example
107
+ * "IMPORTANT NOTE: body data is completely ignored for GET requests, any code
108
+ * requiring it should switch to use POST."
109
+ *
110
+ * let req = { 'params': { 'foo':'bar' }, 'query': {}, 'body': {} };
111
+ * req.hasParams // true
112
+ * req.hasQuery // false
113
+ * req.hasBody // false
114
+ */
115
+ static addExistenceProps (req, res, next) {
116
+ if (req.method === 'GET') {
117
+ req.body = {}
118
+ }
119
+ const storeVal = (key, exists) => {
120
+ req[`has${_.capitalize(key)}`] = exists
121
+ }
122
+ ['body', 'params', 'query'].forEach(attr => {
123
+ if (!req[attr]) {
124
+ return storeVal(attr, true)
125
+ }
126
+ const entries = Object.entries(req[attr])
127
+ let deleted = 0
128
+ if (entries.length === 0) {
129
+ return storeVal(attr, false)
130
+ }
131
+ entries.forEach(([key, val]) => {
132
+ if (val === undefined || val === null) {
133
+ delete req[attr][key]
134
+ deleted++
135
+ }
136
+ })
137
+ storeVal(attr, deleted < entries.length)
138
+ })
139
+ next()
140
+ }
141
+
142
+ /**
143
+ * Handles restriction of routes marked as internal
144
+ * @param {external:ExpressRequest} req
145
+ * @param {external:ExpressResponse} res
146
+ * @param {Function} next
147
+ */
148
+ static async handleInternalRoutes (req, res, next) {
149
+ const server = await App.instance.waitForModule('server')
150
+ const isInternalIp = server.getConfig('host') === req.ip || req.ip === '127.0.0.1' || req.ip === '::1'
151
+ if (req.routeConfig.internal && !isInternalIp) {
152
+ return next(App.instance.errors.UNAUTHORISED.setData({ url: req.originalUrl, method: req.method }))
153
+ }
154
+ next()
155
+ }
156
+
157
+ /**
158
+ * Caches the route config on the incoming request
159
+ * @param {Route} routeConfig
160
+ * @return {Function}
161
+ */
162
+ static cacheRouteConfig (routeConfig) {
163
+ return (req, res, next) => {
164
+ req.routeConfig = routeConfig
165
+ next()
166
+ }
167
+ }
168
+ }
169
+ /** @ignore */ function getEndpoints (r) {
170
+ return r.routes.map(route => {
171
+ return {
172
+ url: `${r.url}${route.route}`,
173
+ accepted_methods: Object.keys(route.handlers).reduce((memo, method) => {
174
+ return {
175
+ ...memo,
176
+ [method]: route?.meta?.[method] ?? {}
177
+ }
178
+ }, {})
179
+ }
180
+ })
181
+ }
182
+ /** @ignore */ function getRelativeRoute (relFrom, relTo) {
183
+ if (relFrom === relTo) {
184
+ return `${relFrom.route}_`
185
+ }
186
+ let route = ''
187
+ for (let r = relTo; r !== relFrom; r = r.parentRouter) route = `${r.root}_${route}`
188
+ return route
189
+ }
190
+
191
+ export default ServerUtils
package/lib/typedefs.js CHANGED
@@ -1,57 +1,57 @@
1
- /**
2
- * This file exists to define the below types for documentation purposes.
3
- */
4
- /**
5
- * Built-in HTTP server
6
- * @memberof server
7
- * @external HttpServer
8
- * @see {@link https://nodejs.org/api/http.html#http_class_http_server}
9
- */
10
- /**
11
- * Express.js top-level application
12
- * @memberof server
13
- * @external ExpressApp
14
- * @see {@link https://expressjs.com/en/4x/api.html#app}
15
- */
16
- /**
17
- * Express.js HTTP router
18
- * @memberof server
19
- * @external ExpressRouter
20
- * @see {@link https://expressjs.com/en/4x/api.html#router}
21
- */
22
- /**
23
- * Express.js HTTP request
24
- * @memberof server
25
- * @external ExpressRequest
26
- * @see {@link https://expressjs.com/en/4x/api.html#req}
27
- */
28
- /**
29
- * Express.js HTTP response
30
- * @memberof server
31
- * @external ExpressResponse
32
- * @see {@link https://nodejs.org/api/http.html#http_class_http_serverresponse}
33
- */
34
- /**
35
- * Defines how an individual API route should be handled
36
- * @memberof server
37
- * @typedef {Object} Route
38
- * @property {String} route The name of the api (this will be used as the API endpoint)
39
- * @property {Object} handlers Object mapping HTTP methods to request handler functions. Note: Any HTTP methods not specified in `handlers` will not be exposed.
40
- * @property {Array<Function>|Function} [handlers.post] POST handlers for the route
41
- * @property {Array<Function>|Function} [handlers.get] GET handlers for the route
42
- * @property {Array<Function>|Function} [handlers.put] PUT handlers for the route
43
- * @property {Array<Function>|Function} [handlers.delete] DELETE handlers for the route
44
- * @example
45
- * {
46
- * route: '/:id?',
47
- * handlers: {
48
- * // can be an array of middleware/handlers
49
- * post: [beforePost, handlePostRequest, afterPost],
50
- * // or an individual function
51
- * get: getRequest,
52
- * put: putRequest,
53
- * // or an in-line function
54
- * delete: (req, res, next) => { next(); }
55
- * }
56
- * }
57
- */
1
+ /**
2
+ * This file exists to define the below types for documentation purposes.
3
+ */
4
+ /**
5
+ * Built-in HTTP server
6
+ * @memberof server
7
+ * @external HttpServer
8
+ * @see {@link https://nodejs.org/api/http.html#http_class_http_server}
9
+ */
10
+ /**
11
+ * Express.js top-level application
12
+ * @memberof server
13
+ * @external ExpressApp
14
+ * @see {@link https://expressjs.com/en/4x/api.html#app}
15
+ */
16
+ /**
17
+ * Express.js HTTP router
18
+ * @memberof server
19
+ * @external ExpressRouter
20
+ * @see {@link https://expressjs.com/en/4x/api.html#router}
21
+ */
22
+ /**
23
+ * Express.js HTTP request
24
+ * @memberof server
25
+ * @external ExpressRequest
26
+ * @see {@link https://expressjs.com/en/4x/api.html#req}
27
+ */
28
+ /**
29
+ * Express.js HTTP response
30
+ * @memberof server
31
+ * @external ExpressResponse
32
+ * @see {@link https://nodejs.org/api/http.html#http_class_http_serverresponse}
33
+ */
34
+ /**
35
+ * Defines how an individual API route should be handled
36
+ * @memberof server
37
+ * @typedef {Object} Route
38
+ * @property {String} route The name of the api (this will be used as the API endpoint)
39
+ * @property {Object} handlers Object mapping HTTP methods to request handler functions. Note: Any HTTP methods not specified in `handlers` will not be exposed.
40
+ * @property {Array<Function>|Function} [handlers.post] POST handlers for the route
41
+ * @property {Array<Function>|Function} [handlers.get] GET handlers for the route
42
+ * @property {Array<Function>|Function} [handlers.put] PUT handlers for the route
43
+ * @property {Array<Function>|Function} [handlers.delete] DELETE handlers for the route
44
+ * @example
45
+ * {
46
+ * route: '/:id?',
47
+ * handlers: {
48
+ * // can be an array of middleware/handlers
49
+ * post: [beforePost, handlePostRequest, afterPost],
50
+ * // or an individual function
51
+ * get: getRequest,
52
+ * put: putRequest,
53
+ * // or an in-line function
54
+ * delete: (req, res, next) => { next(); }
55
+ * }
56
+ * }
57
+ */
package/package.json CHANGED
@@ -1,22 +1,57 @@
1
- {
2
- "name": "adapt-authoring-server",
3
- "version": "0.0.1",
4
- "description": "Provides an Express application routing and more",
5
- "homepage": "https://github.com/adapt-security/adapt-authoring-server",
6
- "license": "GPL-3.0",
7
- "type": "module",
8
- "main": "index.js",
9
- "repository": "github:adapt-security/adapt-authoring-server",
10
- "dependencies": {
11
- "express": "^5.1.0",
12
- "hbs": "^4.2.0",
13
- "lodash": "^4.17.21"
14
- },
15
- "peerDependencies": {
16
- "adapt-authoring-core": "github:adapt-security/adapt-authoring-core"
17
- },
18
- "devDependencies": {
19
- "eslint": "^9.12.0",
20
- "standard": "^17.1.0"
21
- }
22
- }
1
+ {
2
+ "name": "adapt-authoring-server",
3
+ "version": "1.0.0",
4
+ "description": "Provides an Express application routing and more",
5
+ "homepage": "https://github.com/adapt-security/adapt-authoring-server",
6
+ "license": "GPL-3.0",
7
+ "type": "module",
8
+ "main": "index.js",
9
+ "repository": "github:adapt-security/adapt-authoring-server",
10
+ "dependencies": {
11
+ "express": "^5.1.0",
12
+ "hbs": "^4.2.0",
13
+ "lodash": "^4.17.21"
14
+ },
15
+ "peerDependencies": {
16
+ "adapt-authoring-core": "github:adapt-security/adapt-authoring-core"
17
+ },
18
+ "devDependencies": {
19
+ "eslint": "^9.12.0",
20
+ "standard": "^17.1.0",
21
+ "@semantic-release/commit-analyzer": "^9.0.2",
22
+ "@semantic-release/git": "^10.0.1",
23
+ "@semantic-release/github": "^8.0.5",
24
+ "@semantic-release/npm": "^9.0.1",
25
+ "@semantic-release/release-notes-generator": "^10.0.3",
26
+ "conventional-changelog-eslint": "^3.0.9",
27
+ "semantic-release": "^21.0.1",
28
+ "semantic-release-replace-plugin": "^1.2.7"
29
+ },
30
+ "release": {
31
+ "plugins": [
32
+ [
33
+ "@semantic-release/commit-analyzer",
34
+ {
35
+ "preset": "eslint"
36
+ }
37
+ ],
38
+ [
39
+ "@semantic-release/release-notes-generator",
40
+ {
41
+ "preset": "eslint"
42
+ }
43
+ ],
44
+ "@semantic-release/npm",
45
+ "@semantic-release/github",
46
+ [
47
+ "@semantic-release/git",
48
+ {
49
+ "assets": [
50
+ "package.json"
51
+ ],
52
+ "message": "Chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
53
+ }
54
+ ]
55
+ ]
56
+ }
57
+ }