Skip to content

Commit d8bf326

Browse files
committed
chore: some cleanup
1 parent 8fd4f68 commit d8bf326

File tree

5 files changed

+52
-67
lines changed

5 files changed

+52
-67
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@ export interface ReverseProxyConfig {
4848
from: string // domain to proxy from, defaults to localhost:3000
4949
to: string // domain to proxy to, defaults to stacks.localhost
5050
https: boolean | TlsConfig // automatically uses https, defaults to true, also redirects http to https
51+
etcHostsCleanup?: boolean // automatically cleans up /etc/hosts, defaults to false
5152
verbose: boolean // log verbose output, defaults to false
5253
}
5354

5455
const config: ReverseProxyOptions = {
5556
from: 'localhost:3000',
5657
to: 'my-project.localhost',
5758
https: true,
59+
etcHostsCleanup: true,
5860
}
5961

6062
startProxy(config)

bin/cli.ts

+26-66
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,52 @@
1-
import type { ReverseProxyOption } from '../src/types'
2-
import os from 'node:os'
3-
import { CAC, log } from '@stacksjs/cli'
4-
import { readFileSync, writeFileSync } from '@stacksjs/storage'
1+
import { CAC } from '@stacksjs/cli'
52
import { version } from '../package.json'
63
import { config } from '../src/config'
4+
import { httpsConfig } from '../src/https'
75
import { startProxy } from '../src/start'
6+
import { caCertPath, certPath, keyPath } from '../src/utils'
87

98
const cli = new CAC('reverse-proxy')
109

10+
interface ReverseProxyOption {
11+
from: string
12+
to: string
13+
keyPath: string
14+
certPath: string
15+
caCertPath: string
16+
etcHostsCleanup: boolean
17+
verbose: boolean
18+
}
19+
1120
cli
1221
.command('start', 'Start the Reverse Proxy Server')
1322
.option('--from <from>', 'The URL to proxy from', { default: config.from })
1423
.option('--to <to>', 'The URL to proxy to', { default: config.to })
15-
.option('--key-path <path>', 'Absolute path to the SSL key', { default: config.keyPath })
16-
.option('--cert-path <path>', 'Absolute path to the SSL certificate', { default: config.certPath })
24+
.option('--key-path <path>', 'Absolute path to the SSL key', { default: keyPath() })
25+
.option('--cert-path <path>', 'Absolute path to the SSL certificate', { default: certPath() })
26+
.option('--ca-cert-path <path>', 'Absolute path to the SSL CA certificate', { default: caCertPath() })
27+
.option('--etc-hosts-cleanup', 'Cleanup /etc/hosts on exit', { default: config.etcHostsCleanup })
1728
.option('--verbose', 'Enable verbose logging', { default: config.verbose })
1829
.example('reverse-proxy start --from localhost:5173 --to my-project.localhost')
1930
.example('reverse-proxy start --from localhost:3000 --to my-project.localhost/api')
2031
.example('reverse-proxy start --from localhost:3000 --to localhost:3001')
2132
.example('reverse-proxy start --from localhost:5173 --to my-project.test --key-path /absolute/path/to/key --cert-path /absolute/path/to/cert')
2233
.action(async (options?: ReverseProxyOption) => {
34+
const https = {
35+
...httpsConfig(),
36+
keyPath: options?.keyPath || keyPath(),
37+
certPath: options?.certPath || certPath(),
38+
caCertPath: options?.caCertPath || caCertPath(),
39+
}
40+
2341
startProxy({
2442
from: options?.from,
2543
to: options?.to,
26-
keyPath: options?.keyPath,
27-
certPath: options?.certPath,
44+
https,
45+
etcHostsCleanup: options?.etcHostsCleanup,
2846
verbose: options?.verbose,
2947
})
3048
})
3149

32-
cli
33-
.command(
34-
'update:etc-hosts',
35-
'Update the /etc/hosts file with the proxy domains. Please note, this command requires sudo/admin permissions.',
36-
)
37-
.alias('update-etc-hosts')
38-
.example('sudo reverse-proxy update:etc-hosts')
39-
.example('sudo reverse-proxy update-etc-hosts')
40-
.action(async () => {
41-
log.info('Ensuring /etc/hosts file covers the proxy domain/s...')
42-
43-
const hostsFilePath = os.platform() === 'win32' ? 'C:\\Windows\\System32\\drivers\\etc\\hosts' : '/etc/hosts'
44-
45-
if (config && typeof config === 'object') {
46-
const entriesToAdd = Object.entries(config).map(
47-
([from, to]) => `127.0.0.1 ${to} # reverse-proxy mapping for ${from}`,
48-
)
49-
// Ensure "127.0.0.1 localhost" is in the array
50-
entriesToAdd.push('127.0.0.1 localhost # essential localhost mapping')
51-
52-
try {
53-
let currentHostsContent = readFileSync(hostsFilePath, 'utf8')
54-
let updated = false
55-
56-
for (const entry of entriesToAdd) {
57-
const [ip, host] = entry.split(' ', 2)
58-
// Use a regex to match the line with any amount of whitespace between IP and host
59-
const regex = new RegExp(`^${ip}\\s+${host.split(' ')[0]}(\\s|$)`, 'm')
60-
// Check if the entry (domain) is already in the file
61-
if (!regex.test(currentHostsContent)) {
62-
// If not, append it
63-
currentHostsContent += `\n${entry}`
64-
updated = true
65-
}
66-
else {
67-
log.info(`Entry for ${host} already exists in the hosts file.`)
68-
}
69-
}
70-
71-
if (updated) {
72-
writeFileSync(hostsFilePath, currentHostsContent, 'utf8')
73-
log.success('Hosts file updated with latest proxy domains.')
74-
}
75-
else {
76-
log.info('No new entries were added to the hosts file.')
77-
}
78-
}
79-
catch (error: unknown) {
80-
if ((error as NodeJS.ErrnoException).code === 'EACCES')
81-
console.error('Permission denied. Please run this command with administrative privileges.')
82-
else console.error(`An error occurred: ${(error as NodeJS.ErrnoException).message}`)
83-
}
84-
}
85-
else {
86-
console.log('No proxies found. Is your config configured properly?')
87-
}
88-
})
89-
9050
cli.command('version', 'Show the version of the Reverse Proxy CLI').action(() => {
9151
console.log(version)
9252
})

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './hosts'
33
export * from './https'
44
export * from './start'
55
export * from './types'
6+
export * from './utils'

src/types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export interface SSLConfig {
1515
key: string
1616
cert: string
1717
ca?: string | string[]
18-
secureOptions?: number
1918
}
2019

2120
export interface ProxySetupOptions extends Omit<ReverseProxyOption, 'from'> {

src/utils.ts

+23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
1+
import { homedir } from 'node:os'
2+
import { join } from 'node:path'
13
import { config } from './config'
24

5+
export function keyPath(): string {
6+
if (typeof config.https === 'boolean')
7+
return join(homedir(), '.stacks', 'ssl', `stacks.localhost.key`)
8+
9+
return config.https.keyPath
10+
}
11+
12+
export function certPath(): string {
13+
if (typeof config.https === 'boolean')
14+
return join(homedir(), '.stacks', 'ssl', `stacks.localhost.crt`)
15+
16+
return config.https.certPath
17+
}
18+
19+
export function caCertPath(): string {
20+
if (typeof config.https === 'boolean')
21+
return join(homedir(), '.stacks', 'ssl', `stacks.localhost.ca.crt`)
22+
23+
return config.https.caCertPath
24+
}
25+
326
export function debugLog(category: string, message: string, verbose?: boolean): void {
427
if (verbose || config.verbose) {
528
// eslint-disable-next-line no-console

0 commit comments

Comments
 (0)