Skip to content

Commit f6537b8

Browse files
committed
feat: configurable base path
closes #3
1 parent b197098 commit f6537b8

File tree

6 files changed

+59
-20
lines changed

6 files changed

+59
-20
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,13 @@ await app.start()
127127
"secure": {
128128
"cert": "ssl/cert.pem",
129129
"key": "ssl/key.pem"
130-
}
130+
},
131+
// Optional. Defaults to '/'
132+
"base": "/",
133+
// Optional. Defaults to '/'
134+
"api": "/",
135+
// Optional. Defaults to '/healthcheck'. Pass null to disable
136+
"healthcheck": "/health"
131137
}
132138
],
133139
// Remote registry

config.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"secure": {
77
"cert": "ssl/cert.pem",
88
"key": "ssl/key.pem"
9-
}
9+
},
10+
"healthcheck": null,
11+
"api": "/registry"
1012
}
1113
],
1214
"registry": "https://registry.npmmirror.com",

src/main/js/app.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ import { getConfig } from './config.js'
1616
export const createApp = (cfg) => {
1717
const config = getConfig(cfg)
1818
const servers = config.server.map(s => {
19-
const router = createRouter([
20-
[ ctx({...config, server: s}) ],
21-
[ timeout ],
22-
[ trace ],
23-
['GET', '/healthcheck/', healthcheck],
19+
const api = createRouter([
2420
[
2521
'*',
2622
[
@@ -37,10 +33,20 @@ export const createApp = (cfg) => {
3733
],
3834
firewall
3935
],
40-
[ proxy ],
41-
[ notFound ],
42-
[ errorBoundary ],
43-
])
36+
proxy,
37+
errorBoundary,
38+
], s.api)
39+
40+
const router = createRouter([
41+
ctx({...config, server: s}),
42+
timeout,
43+
trace,
44+
['GET', s.healthcheck, healthcheck],
45+
api,
46+
notFound,
47+
errorBoundary,
48+
], s.base)
49+
4450
return createServer({...s, router})
4551
})
4652
return {

src/main/js/config.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'node:fs'
22
import {strict as assert} from 'node:assert'
3-
import {asRegExp, asArray} from './util.js'
3+
import {asRegExp, asArray, normalizePath} from './util.js'
44
import { semver } from './semver.js'
55

66
const populate = (config) => {
@@ -9,6 +9,9 @@ const populate = (config) => {
99
const server = asArray(config.server).map(({
1010
host,
1111
port,
12+
base = '/',
13+
api = '/',
14+
healthcheck = '/healthcheck',
1215
secure: _secure,
1316
keepAliveTimeout = 61_000,
1417
headersTimeout = 62_000,
@@ -22,12 +25,15 @@ const populate = (config) => {
2225
key: fs.readFileSync(_secure.key, 'utf8'),
2326
cert: fs.readFileSync(_secure.cert, 'utf8'),
2427
} : null
25-
const entrypoint = `${secure ? 'https' : 'http'}://${host}:${port}`
28+
const entrypoint = normalizePath(`${secure ? 'https' : 'http'}://${host}:${port}${base}${api}`)
2629

2730
return {
2831
secure,
2932
host,
3033
port,
34+
base,
35+
healthcheck,
36+
api,
3137
entrypoint,
3238
requestTimeout,
3339
headersTimeout,

src/main/js/http/router.js

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {asArray, once} from '../util.js'
1+
import {asArray, normalizePath, once} from '../util.js'
2+
3+
const normalizeRoute = (item) => {
4+
const [m, p, cb] = asArray(item)
25

3-
const normalizeRoute = ([m, p, cb]) => {
46
if (typeof m === 'function') {
5-
return ['*', [null], m]
7+
return ['*', [true], m]
68
}
79

810
if (typeof p === 'function') {
9-
return [m, [null], p]
11+
return [m, [true], p]
1012
}
1113

1214
return [m, asArray(p), cb]
@@ -16,8 +18,12 @@ const matchMethod = (method, expected) =>
1618
method === expected || expected === '*' || expected === 'ALL'
1719

1820
const matchUrl = (url, [pattern]) => {
21+
if (pattern === null) {
22+
return false
23+
}
24+
1925
if (typeof pattern === 'string') {
20-
return url.startsWith(pattern)
26+
return url === pattern || url.startsWith(pattern) && url.charAt(pattern.length) === '/'
2127
}
2228

2329
if (pattern instanceof RegExp) {
@@ -27,8 +33,14 @@ const matchUrl = (url, [pattern]) => {
2733
return true
2834
}
2935

30-
export const createRouter = (routes) => async (req, res) => {
31-
const url = req.url
36+
export const createRouter = (routes, base = '/') => async (req, res, next = () => {}) => {
37+
if (req.url.startsWith(base)) {
38+
req.url = req.url.replace(base, '/')
39+
} else {
40+
return next()
41+
}
42+
43+
const url = normalizePath(req.url)
3244
const matched = routes
3345
.map(normalizeRoute)
3446
.filter(([method, pattern]) => matchMethod(req.method, method) && matchUrl(url, pattern))

src/main/js/util.js

+7
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@ export const asArray = v => Array.isArray(v) ? v : [v]
1818
export const asRegExp = v => v instanceof RegExp
1919
? v
2020
: new RegExp(`^${v.replace(/\*/g, '.+')}$`, 'i')
21+
22+
export const normalizePath = (url) => url.length > 1
23+
? (url + '/')
24+
.replace(/\/+/g, '/')
25+
.replace(':/', '://')
26+
.slice(0, -1)
27+
: url

0 commit comments

Comments
 (0)