unplugin-https-reverse-proxy 0.0.4 → 0.0.7

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "unplugin-https-reverse-proxy",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.7",
5
5
  "packageManager": "pnpm@8.6.12",
6
6
  "description": "",
7
7
  "license": "MIT",
@@ -70,7 +70,9 @@
70
70
  }
71
71
  },
72
72
  "files": [
73
- "dist"
73
+ "dist",
74
+ "scripts/postinstall.ts",
75
+ "src"
74
76
  ],
75
77
  "peerDependencies": {
76
78
  "@nuxt/kit": "^3",
@@ -0,0 +1,15 @@
1
+ import { existsSync, unlinkSync } from 'node:fs'
2
+ import { download } from '../src/caddy'
3
+ import { caddyPath } from '../src/caddy/constants'
4
+
5
+ async function run() {
6
+ try {
7
+ existsSync(caddyPath) && unlinkSync(caddyPath)
8
+ await download()
9
+ }
10
+ catch (e: any) {
11
+ console.log(e.code)
12
+ }
13
+ }
14
+
15
+ run()
@@ -0,0 +1,117 @@
1
+ import { join } from 'node:path'
2
+ import process from 'node:process'
3
+
4
+ const TEMP_DIR = process.env.TEMP || process.env.TMPDIR || '/tmp'
5
+ export const caddyPath = join(TEMP_DIR, `caddy${process.platform === 'win32' ? '.exe' : ''}`)
6
+ export const caddyFilePath = join(TEMP_DIR, 'CADDYFILE')
7
+
8
+ export const supportList = [
9
+ {
10
+ platform: 'dragonfly',
11
+ arch: 'amd64',
12
+ },
13
+ {
14
+ platform: 'freebsd',
15
+ arch: 'amd64',
16
+ },
17
+ {
18
+ platform: 'freebsd',
19
+ arch: 'arm-6',
20
+ },
21
+ {
22
+ platform: 'freebsd',
23
+ arch: 'arm-7',
24
+ },
25
+ {
26
+ platform: 'freebsd',
27
+ arch: 'arm64',
28
+ },
29
+ {
30
+ platform: 'linux',
31
+ arch: 'amd64',
32
+ },
33
+ {
34
+ platform: 'linux',
35
+ arch: 'arm-5',
36
+ },
37
+ {
38
+ platform: 'linux',
39
+ arch: 'arm-6',
40
+ },
41
+ {
42
+ platform: 'linux',
43
+ arch: 'arm-7',
44
+ },
45
+ {
46
+ platform: 'linux',
47
+ arch: 'arm64',
48
+ },
49
+ {
50
+ platform: 'linux',
51
+ arch: 'mips',
52
+ },
53
+ {
54
+ platform: 'linux',
55
+ arch: 'mips64',
56
+ },
57
+ {
58
+ platform: 'linux',
59
+ arch: 'mips64le',
60
+ },
61
+ {
62
+ platform: 'linux',
63
+ arch: 'mipsle',
64
+ },
65
+ {
66
+ platform: 'linux',
67
+ arch: 'ppc64',
68
+ },
69
+ {
70
+ platform: 'linux',
71
+ arch: 'ppc64le',
72
+ },
73
+ {
74
+ platform: 'linux',
75
+ arch: 's390x',
76
+ },
77
+ {
78
+ platform: 'darwin',
79
+ arch: 'amd64',
80
+ },
81
+ {
82
+ platform: 'darwin',
83
+ arch: 'arm64',
84
+ },
85
+ {
86
+ platform: 'openbsd',
87
+ arch: 'amd64',
88
+ },
89
+ {
90
+ platform: 'openbsd',
91
+ arch: 'arm-6',
92
+ },
93
+ {
94
+ platform: 'openbsd',
95
+ arch: 'arm-7',
96
+ },
97
+ {
98
+ platform: 'openbsd',
99
+ arch: 'arm64',
100
+ },
101
+ {
102
+ platform: 'windows',
103
+ arch: 'amd64',
104
+ },
105
+ {
106
+ platform: 'windows',
107
+ arch: 'arm-6',
108
+ },
109
+ {
110
+ platform: 'windows',
111
+ arch: 'arm-7',
112
+ },
113
+ {
114
+ platform: 'windows',
115
+ arch: 'arm64',
116
+ },
117
+ ]
@@ -0,0 +1,212 @@
1
+ import { platform } from 'node:os'
2
+ import process from 'node:process'
3
+ import { chmodSync, createWriteStream, existsSync, unlinkSync } from 'node:fs'
4
+ import { spawn } from 'node:child_process'
5
+ import { got } from 'got-cjs'
6
+ import { HttpProxyAgent } from 'http-proxy-agent'
7
+ import { HttpsProxyAgent } from 'https-proxy-agent'
8
+ import * as hostile from 'hostile'
9
+ import kill from 'kill-port'
10
+ import { caddyPath, supportList } from './constants'
11
+ import { tryPort } from './utils'
12
+
13
+ export async function download() {
14
+ if (await testCaddy())
15
+ return caddyPath
16
+
17
+ return new Promise<string>((resolve, reject) => {
18
+ // https://caddyserver.com/api/download?os=darwin&arch=amd64
19
+
20
+ const file = createWriteStream(caddyPath)
21
+
22
+ let _platform = platform().toString()
23
+ // win32 -> windows
24
+ _platform === 'win32' && (_platform = 'windows')
25
+ let _arch = process.arch.toString()
26
+ // x64 -> amd64
27
+ _arch === 'x64' && (_arch = 'amd64')
28
+
29
+ const support = supportList.find(item => item.platform === _platform && item.arch === _arch)
30
+
31
+ if (!support)
32
+ return reject(new Error('not support'))
33
+
34
+ const dowmloadLink = `https://caddyserver.com/api/download?os=${support.platform}&arch=${support.arch}`
35
+
36
+ existsSync(caddyPath) && unlinkSync(caddyPath)
37
+
38
+ const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy
39
+ const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy
40
+
41
+ const httpAgent = httpProxy ? new HttpProxyAgent(httpProxy) : undefined
42
+ const httpsAgent = httpsProxy ? new HttpsProxyAgent(httpsProxy) : undefined
43
+
44
+ got.stream(dowmloadLink, {
45
+ agent: {
46
+ http: httpAgent,
47
+ https: httpsAgent,
48
+ },
49
+ }).pipe(file).on('finish', () => {
50
+ if (process.platform === 'win32')
51
+ return resolve(caddyPath)
52
+ // chmod +x
53
+ chmodSync(caddyPath, 0o755)
54
+ resolve(caddyPath)
55
+ }).on('error', (err) => {
56
+ // console.log(err)
57
+ reject(err)
58
+ })
59
+ })
60
+ }
61
+
62
+ function testCaddy() {
63
+ return new Promise((resolve, reject) => {
64
+ if (!existsSync(caddyPath))
65
+ return resolve(false)
66
+ chmodSync(caddyPath, 0o755)
67
+ const child = process.platform === 'win32' ? spawn(caddyPath, []) : spawn('sudo', [caddyPath])
68
+ child.on('close', () => {
69
+ return resolve(true)
70
+ })
71
+ child.on('error', (err) => {
72
+ return reject(err)
73
+ })
74
+ child.stdout.on('data', (_data) => {
75
+ return resolve(true)
76
+ })
77
+ })
78
+ }
79
+
80
+ interface RunOptions {
81
+ restore: boolean
82
+ base: string
83
+ }
84
+
85
+ export class CaddyInstant {
86
+ private inited = false
87
+
88
+ constructor() {
89
+ testCaddy().then(() => {
90
+ this.inited = true
91
+ })
92
+ }
93
+
94
+ async init() {
95
+ await download()
96
+ this.inited = true
97
+ }
98
+
99
+ private async updateHost(ip: string, host: string) {
100
+ ['localhost', '0.0.0.0'].includes(ip) && (ip = '127.0.0.1')
101
+ return new Promise<boolean>((resolve, reject) => {
102
+ hostile.set(ip, host, (err) => {
103
+ if (err) {
104
+ return reject(err)
105
+ }
106
+ else {
107
+ if (process.platform === 'win32') {
108
+ spawn('ipconfig', ['/flushdns']).on('error', (err) => {
109
+ return reject(err)
110
+ }).on('close', () => {
111
+ resolve(true)
112
+ })
113
+ }
114
+ else {
115
+ spawn('sudo', ['killall', '-HUP', 'mDNSResponder']).on('error', (err) => {
116
+ return reject(err)
117
+ }).on('close', () => {
118
+ resolve(true)
119
+ })
120
+ }
121
+ }
122
+ })
123
+ })
124
+ }
125
+
126
+ private async restoreHost(ip: string, host: string) {
127
+ ['localhost', '0.0.0.0'].includes(ip) && (ip = '127.0.0.1')
128
+ return new Promise<boolean>((resolve, reject) => {
129
+ hostile.remove(ip, host, (err) => {
130
+ if (err) {
131
+ return reject(err)
132
+ }
133
+ else {
134
+ if (process.platform === 'win32') {
135
+ spawn('ipconfig', ['/flushdns']).on('error', (err) => {
136
+ return reject(err)
137
+ }).on('close', () => {
138
+ resolve(true)
139
+ })
140
+ }
141
+ else {
142
+ spawn('sudo', ['killall', '-HUP', 'mDNSResponder']).on('error', (err) => {
143
+ return reject(err)
144
+ }).on('close', () => {
145
+ resolve(true)
146
+ })
147
+ }
148
+ }
149
+ })
150
+ })
151
+ }
152
+
153
+ /**
154
+ * @param source like `127.0.0.1:8080`
155
+ * @param target like `test.abc.com`. And if you provide a port ,it will be ignore.
156
+ * @returns
157
+ */
158
+ async run(source: string, target: string, options?: Partial<RunOptions>) {
159
+ source = source.replace('0.0.0.0', '127.0.0.1').replace('localhost', '127.0.0.1')
160
+ const {
161
+ restore = true,
162
+ base = '/',
163
+ } = options || {}
164
+
165
+ if (!this.inited)
166
+ await this.init()
167
+
168
+ if (!await this.updateHost(source.split(':')[0], target.split(':')[0]))
169
+ throw new Error('update host failed')
170
+
171
+ if (await tryPort(443))
172
+ await kill(443, 'tcp')
173
+ if (process.platform === 'win32' && await tryPort(80))
174
+ await kill(80, 'tcp')
175
+
176
+ return new Promise<() => Promise<void>>((resolve, reject) => {
177
+ // caddy reverse-proxy --from target --to source --internal-certs
178
+ const child = process.platform !== 'win32'
179
+ ? spawn('sudo', [caddyPath, 'reverse-proxy', '--from', `${target.split(':')[0]}`, '--to', `${source}`, '--internal-certs'])
180
+ : spawn(caddyPath, ['reverse-proxy', '--from', `${target.split(':')[0]}`, '--to', `${source}`, '--internal-certs'])
181
+
182
+ child.on('error', (err) => {
183
+ return reject(err)
184
+ })
185
+
186
+ child.stderr.on('data', (data) => {
187
+ if ((data.toString() as string).includes('Error')) {
188
+ child.kill()
189
+ return reject(data.toString())
190
+ }
191
+ })
192
+
193
+ child.stdout.on('data', (_data) => {
194
+ // eslint-disable-next-line no-console
195
+ console.log(_data.toString())
196
+ resolve(async () => {
197
+ if (!restore)
198
+ return
199
+
200
+ if (await this.restoreHost(source.split(':')[0], target.split(':')[0])) {
201
+ // eslint-disable-next-line no-console
202
+ console.log('restore host success\n')
203
+ }
204
+ else {
205
+ console.error('restore host failed\n')
206
+ }
207
+ child.kill()
208
+ })
209
+ })
210
+ })
211
+ }
212
+ }
@@ -0,0 +1,26 @@
1
+ import { createServer } from 'node:net'
2
+
3
+ /**
4
+ * @returns whether the port is in use
5
+ */
6
+ export function tryPort(port: number) {
7
+ return new Promise<boolean>((resolve, reject) => {
8
+ try {
9
+ const server = createServer().listen(port)
10
+ server.on('listening', () => {
11
+ server.close()
12
+ resolve(false)
13
+ })
14
+ server.on('error', (err) => {
15
+ // @ts-expect-error err.code
16
+ if (err.code === 'EADDRINUSE')
17
+ resolve(true)
18
+ server.close()
19
+ })
20
+ }
21
+ catch (e) {
22
+ // console.log(e)
23
+ resolve(true)
24
+ }
25
+ })
26
+ }
package/src/esbuild.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { createEsbuildPlugin } from 'unplugin'
2
+ import { unpluginFactory } from '.'
3
+
4
+ export default createEsbuildPlugin(unpluginFactory)
package/src/index.ts ADDED
@@ -0,0 +1,105 @@
1
+ import process from 'node:process'
2
+ import type { UnpluginFactory } from 'unplugin'
3
+ import { createUnplugin } from 'unplugin'
4
+ import type { ResolvedConfig } from 'vite'
5
+ import c from 'picocolors'
6
+ import type { Options } from './types'
7
+ import { CaddyInstant } from './caddy'
8
+
9
+ let config: ResolvedConfig
10
+
11
+ let caddy: CaddyInstant
12
+
13
+ export const unpluginFactory: UnpluginFactory<Options> = options => ({
14
+ name: 'unplugin-https-reverse-proxy',
15
+ enforce: 'post',
16
+ vite: {
17
+ configResolved(_config) {
18
+ config = _config
19
+ },
20
+ configureServer(server) {
21
+ const {
22
+ target = '',
23
+ } = options
24
+ if (!target) {
25
+ // eslint-disable-next-line no-console
26
+ console.log('please provide target')
27
+ return
28
+ }
29
+ server.printUrls = () => {
30
+ let source = `localhost:${config.server.port}`
31
+ const url = server.resolvedUrls?.local[0]
32
+ if (url) {
33
+ const u = new URL(url)
34
+ source = u.host
35
+ }
36
+ const base = server.config.base || '/'
37
+ try {
38
+ const caddy = new CaddyInstant()
39
+ caddy.run(source, target, {
40
+ base,
41
+ }).then((stop) => {
42
+ const colorUrl = (url: string) => c.green(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`))
43
+ // eslint-disable-next-line no-console
44
+ console.log(` ${c.green('➜')} ${c.bold('run caddy reverse proxy success')}: ${colorUrl(`https://${target}${base}`)}`)
45
+
46
+ process.on('SIGINT', async () => {
47
+ await stop()
48
+ server.close()
49
+ process.exit()
50
+ })
51
+ })
52
+ }
53
+ catch (e) {
54
+ console.error(e)
55
+ }
56
+ }
57
+ },
58
+ },
59
+ webpack(compiler) {
60
+ const {
61
+ target = '',
62
+ } = options
63
+ if (!target) {
64
+ // eslint-disable-next-line no-console
65
+ console.log('please provide target')
66
+ return
67
+ }
68
+ try {
69
+ // @ts-expect-error vuecli
70
+ const devServer = compiler.options.devServer || process.VUE_CLI_SERVICE.projectOptions.devServer
71
+ const source = `${devServer?.host || '127.0.0.1'}:${devServer?.port || '8080'}`
72
+
73
+ if (caddy)
74
+ return
75
+
76
+ caddy = new CaddyInstant()
77
+
78
+ caddy.run(source, target).then((stop) => {
79
+ const colorUrl = (url: string) => c.green(url.replace(/:(\d+)\//, (_, port) => `:${c.bold(port)}/`))
80
+
81
+ compiler.hooks.done.tap('unplugin-https-reverse-proxy', (stats) => {
82
+ if (stats.hasErrors())
83
+ return
84
+
85
+ // eslint-disable-next-line no-console
86
+ console.log(` ${c.green('➜')} ${c.bold('run caddy reverse proxy success')}: ${colorUrl(`https://${target}`)}`)
87
+ })
88
+
89
+ process.on('SIGINT', async () => {
90
+ await stop()
91
+ process.exit()
92
+ })
93
+ }).catch((e) => {
94
+ throw e
95
+ })
96
+ }
97
+ catch (e) {
98
+ console.error(e)
99
+ }
100
+ },
101
+ })
102
+
103
+ export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory)
104
+
105
+ export default unplugin
package/src/nuxt.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { addVitePlugin, addWebpackPlugin, defineNuxtModule } from '@nuxt/kit'
2
+ import vite from './vite'
3
+ import webpack from './webpack'
4
+ import type { Options } from './types'
5
+ import '@nuxt/schema'
6
+
7
+ export interface ModuleOptions extends Options {
8
+
9
+ }
10
+
11
+ export default defineNuxtModule<ModuleOptions>({
12
+ meta: {
13
+ name: 'nuxt-unplugin-https-reverse-proxy',
14
+ configKey: 'unpluginHttpsReverseProxy',
15
+ },
16
+ setup(options, _nuxt) {
17
+ addVitePlugin(() => vite(options))
18
+ addWebpackPlugin(() => webpack(options))
19
+
20
+ // ...
21
+ },
22
+ })
package/src/rollup.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { createRollupPlugin } from 'unplugin'
2
+ import { unpluginFactory } from '.'
3
+
4
+ export default createRollupPlugin(unpluginFactory)
package/src/types.ts ADDED
@@ -0,0 +1,4 @@
1
+ export interface Options {
2
+ // define your plugin options here
3
+ target: string
4
+ }
package/src/vite.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { createVitePlugin } from 'unplugin'
2
+ import { unpluginFactory } from '.'
3
+
4
+ export default createVitePlugin(unpluginFactory)
package/src/webpack.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { createWebpackPlugin } from 'unplugin'
2
+ import { unpluginFactory } from '.'
3
+
4
+ export default createWebpackPlugin(unpluginFactory)