piclist 1.9.14 → 2.0.1

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.
Files changed (80) hide show
  1. package/.github/workflows/docker.yml +5 -5
  2. package/CHANGELOG.md +19 -0
  3. package/License +22 -22
  4. package/README.md +1 -1
  5. package/README_cn.md +1 -1
  6. package/bin/picgo +9 -7
  7. package/bin/picgo-server +180 -161
  8. package/dist/core/Lifecycle.d.ts +35 -36
  9. package/dist/core/PicGo.d.ts +52 -52
  10. package/dist/i18n/en.d.ts +2 -2
  11. package/dist/i18n/index.d.ts +19 -17
  12. package/dist/i18n/zh-CN.d.ts +293 -289
  13. package/dist/i18n/zh-TW.d.ts +2 -2
  14. package/dist/index.d.ts +10 -10
  15. package/dist/index.js +2 -0
  16. package/dist/lib/Commander.d.ts +22 -22
  17. package/dist/lib/LifecyclePlugins.d.ts +26 -26
  18. package/dist/lib/Logger.d.ts +19 -19
  19. package/dist/lib/PluginHandler.d.ts +10 -10
  20. package/dist/lib/PluginLoader.d.ts +27 -27
  21. package/dist/lib/Request.d.ts +12 -12
  22. package/dist/plugins/beforetransformer/compress.d.ts +5 -5
  23. package/dist/plugins/beforetransformer/skipProcess.d.ts +5 -5
  24. package/dist/plugins/beforetransformer/watermark.d.ts +5 -5
  25. package/dist/plugins/beforeupload/buildInRename.d.ts +5 -5
  26. package/dist/plugins/commander/config.d.ts +3 -3
  27. package/dist/plugins/commander/i18n.d.ts +3 -3
  28. package/dist/plugins/commander/index.d.ts +3 -3
  29. package/dist/plugins/commander/pluginHandler.d.ts +3 -3
  30. package/dist/plugins/commander/proxy.d.ts +3 -3
  31. package/dist/plugins/commander/setting.d.ts +5 -5
  32. package/dist/plugins/commander/upload.d.ts +3 -3
  33. package/dist/plugins/commander/use.d.ts +3 -3
  34. package/dist/plugins/commander/utils.d.ts +2 -2
  35. package/dist/plugins/transformer/base64.d.ts +5 -5
  36. package/dist/plugins/transformer/index.d.ts +5 -5
  37. package/dist/plugins/transformer/path.d.ts +5 -5
  38. package/dist/plugins/uploader/advancedplist.d.ts +2 -2
  39. package/dist/plugins/uploader/alist.d.ts +2 -2
  40. package/dist/plugins/uploader/aliyun.d.ts +2 -2
  41. package/dist/plugins/uploader/awss3plist.d.ts +2 -2
  42. package/dist/plugins/uploader/github.d.ts +2 -2
  43. package/dist/plugins/uploader/imgur.d.ts +2 -2
  44. package/dist/plugins/uploader/index.d.ts +5 -5
  45. package/dist/plugins/uploader/local.d.ts +2 -2
  46. package/dist/plugins/uploader/lsky.d.ts +20 -21
  47. package/dist/plugins/uploader/piclist.d.ts +2 -2
  48. package/dist/plugins/uploader/qiniu.d.ts +2 -2
  49. package/dist/plugins/uploader/s3/uploader.d.ts +25 -24
  50. package/dist/plugins/uploader/s3/utils.d.ts +9 -10
  51. package/dist/plugins/uploader/sftp.d.ts +2 -2
  52. package/dist/plugins/uploader/smms.d.ts +2 -2
  53. package/dist/plugins/uploader/tcyun.d.ts +8 -8
  54. package/dist/plugins/uploader/upyun.d.ts +2 -2
  55. package/dist/plugins/uploader/utils.d.ts +26 -26
  56. package/dist/plugins/uploader/webdav.d.ts +2 -2
  57. package/dist/types/index.d.ts +572 -693
  58. package/dist/types/oldRequest.d.ts +15 -15
  59. package/dist/utils/common.d.ts +42 -119
  60. package/dist/utils/createContext.d.ts +6 -6
  61. package/dist/utils/db.d.ts +17 -15
  62. package/dist/utils/enum.d.ts +27 -27
  63. package/dist/utils/eventBus.d.ts +3 -4
  64. package/dist/utils/getClipboardImage.d.ts +4 -4
  65. package/dist/utils/sshClient.d.ts +16 -16
  66. package/dist/utils/static.d.ts +1 -1
  67. package/docker-compose.yaml +11 -11
  68. package/eslint.config.js +104 -0
  69. package/package.json +61 -78
  70. package/rollup.config.js +25 -42
  71. package/.eslintignore +0 -6
  72. package/.eslintrc.js +0 -50
  73. package/.github/workflows/alpha.yml +0 -22
  74. package/.github/workflows/main.yml +0 -22
  75. package/.github/workflows/manually.yml +0 -19
  76. package/dist/index.cjs.js +0 -2
  77. package/dist/index.esm.js +0 -2
  78. package/dist/plugins/commander/init.d.ts +0 -3
  79. package/dist/utils/initUtils.d.ts +0 -26
  80. package/dist/utils/interfaces.d.ts +0 -201
@@ -11,19 +11,19 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: Checkout Repository
14
- uses: actions/checkout@v2
14
+ uses: actions/checkout@v4
15
15
 
16
16
  - name: Set up Docker Buildx
17
- uses: docker/setup-buildx-action@v1
17
+ uses: docker/setup-buildx-action@v3
18
18
 
19
19
  - name: Login to Docker Hub
20
- uses: docker/login-action@v1
20
+ uses: docker/login-action@v3
21
21
  with:
22
22
  username: ${{ secrets.DOCKERHUB_USERNAME }}
23
23
  password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }}
24
24
 
25
25
  - name: Build and Push Docker Image
26
- uses: docker/build-push-action@v2
26
+ uses: docker/build-push-action@v6
27
27
  with:
28
28
  context: .
29
29
  file: ./Dockerfile
@@ -33,7 +33,7 @@ jobs:
33
33
 
34
34
  - name: Build and Push Docker Image with Tag
35
35
  if: startsWith(github.ref, 'refs/tags/')
36
- uses: docker/build-push-action@v2
36
+ uses: docker/build-push-action@v6
37
37
  with:
38
38
  context: .
39
39
  file: ./Dockerfile
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## (2025-08-20)
2
+
3
+ * :bug: Fix(custom): replace mime-type with mime 18e6b75
4
+
5
+
6
+
7
+ ## (2025-08-12)
8
+
9
+ * :arrow_up: Upgrade(custom): upgrade deps 75e5a11
10
+ * :package: Chore(custom): remove unused files 6be4d68
11
+ * :sparkles: Feature(custom): add image watermark opacity setting a1d41f5
12
+ * :sparkles: Feature(custom): add options for s3 88e2c7f, closes Kuingsmile/Piclist#343
13
+ * :sparkles: Feature(custom): change watermark and image compress order bc380a6
14
+ * :sparkles: Feature(custom): migrate to esm 838cb84
15
+ * :sparkles: Feature(custom): optimize server 0dc69b2
16
+ * :sparkles: Feature(custom): remove cjs support 9cd9674
17
+
18
+
19
+
1
20
  ## :tada: 1.9.14 (2025-06-12)
2
21
 
3
22
 
package/License CHANGED
@@ -1,22 +1,22 @@
1
- MIT License
2
-
3
- Copyright (c) 2018 Molunerfinn
4
- Copyright (c) 2023-present Kuingsmile
5
-
6
- Permission is hereby granted, free of charge, to any person obtaining a copy
7
- of this software and associated documentation files (the "Software"), to deal
8
- in the Software without restriction, including without limitation the rights
9
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- copies of the Software, and to permit persons to whom the Software is
11
- furnished to do so, subject to the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be included in all
14
- copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Molunerfinn
4
+ Copyright (c) 2023-present Kuingsmile
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md CHANGED
@@ -37,7 +37,7 @@ You can refer to the [DeepWiki of PiclList-Core](https://deepwiki.com/Kuingsmile
37
37
 
38
38
  ## Installation
39
39
 
40
- PicList requires Node.js >= 16
40
+ PicList requires Node.js >= 20
41
41
 
42
42
  ### Prerequisites
43
43
 
package/README_cn.md CHANGED
@@ -37,7 +37,7 @@ PicList-Core 是一个功能强大的图片上传工具,提供 CLI 和 API 两
37
37
 
38
38
  ## 安装
39
39
 
40
- PicList 需要 Node.js >= 16
40
+ PicList 需要 Node.js >= 20
41
41
 
42
42
  ### 前置条件
43
43
 
package/bin/picgo CHANGED
@@ -1,18 +1,20 @@
1
1
  #!/usr/bin/env node
2
- const path = require('path')
3
- const minimist = require('minimist')
4
- const os = require('os')
2
+ import { homedir } from 'node:os'
3
+ import { resolve } from 'node:path'
4
+
5
+ import minimist from 'minimist'
6
+
7
+ import { PicGo } from '../dist/index.js'
5
8
 
6
9
  const argv = minimist(process.argv.slice(2))
7
10
  let configPath = argv.c || argv.config || ''
8
11
  if (configPath !== true && configPath !== '') {
9
- configPath = configPath.replace(/^~/, os.homedir())
10
- configPath = path.resolve(configPath)
12
+ configPath = configPath.replace(/^~/, homedir())
13
+ configPath = resolve(configPath)
11
14
  } else {
12
15
  configPath = ''
13
16
  }
14
- const { PicGo } = require('..')
15
- const picgo = new PicGo(configPath)
17
+ const picgo = await PicGo.create(configPath)
16
18
  picgo.registerCommands()
17
19
 
18
20
  try {
package/bin/picgo-server CHANGED
@@ -1,14 +1,18 @@
1
1
  #!/usr/bin/env node
2
- const http = require('http')
3
- const multer = require('multer')
4
- const axios = require('axios')
5
- const minimist = require('minimist')
6
- const fs = require('fs-extra')
7
- const path = require('path')
8
- const os = require('os')
2
+ import http from 'node:http'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+
6
+ import axios from 'axios'
7
+ import fs from 'fs-extra'
8
+ import { ensureDirSync } from 'fs-extra/esm'
9
+ import minimist from 'minimist'
10
+ import multer from 'multer'
11
+
12
+ import { PicGo } from '../dist/index.js'
9
13
 
10
14
  const tempDir = path.join(os.homedir(), '.piclist', 'serverTemp')
11
- fs.ensureDirSync(tempDir)
15
+ ensureDirSync(tempDir)
12
16
 
13
17
  const argv = minimist(process.argv.slice(2))
14
18
 
@@ -16,33 +20,6 @@ let key = ''
16
20
  let defaultPort = 36677
17
21
  let defaultHost = '0.0.0.0'
18
22
 
19
- if (argv.h || argv.help) {
20
- showHelp()
21
- process.exit(0)
22
- }
23
-
24
- if (argv.v || argv.version) {
25
- showVersion()
26
- process.exit(0)
27
- }
28
-
29
- if (argv.k || argv.key) {
30
- key = String(argv.k || argv.key)
31
- }
32
-
33
- if (argv.p || argv.port) {
34
- defaultPort = Number(argv.p || argv.port) || defaultPort
35
- }
36
-
37
- if (argv.host) {
38
- defaultHost = String(argv.host)
39
- }
40
-
41
- function showVersion() {
42
- const pkg = require('../package.json')
43
- console.log(`PicList-Core ${pkg.version}`)
44
- }
45
-
46
23
  function showHelp() {
47
24
  console.log(`
48
25
  Usage: picgo-server [options]
@@ -61,6 +38,31 @@ function showHelp() {
61
38
  `)
62
39
  }
63
40
 
41
+ const ensureHTTPLink = url => {
42
+ return url.startsWith('http') ? url : `http://${url}`
43
+ }
44
+
45
+ function isLoopback(addr = '') {
46
+ return addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1'
47
+ }
48
+
49
+ if (argv.h || argv.help) {
50
+ showHelp()
51
+ process.exit(0)
52
+ }
53
+
54
+ if (argv.k || argv.key) {
55
+ key = String(argv.k || argv.key)
56
+ }
57
+
58
+ if (argv.p || argv.port) {
59
+ defaultPort = Number(argv.p || argv.port) || defaultPort
60
+ }
61
+
62
+ if (argv.host) {
63
+ defaultHost = String(argv.host)
64
+ }
65
+
64
66
  let configPath = argv.c || argv.config || ''
65
67
  if (configPath !== true && configPath !== '') {
66
68
  configPath = configPath.replace(/^~/, os.homedir())
@@ -69,11 +71,10 @@ if (configPath !== true && configPath !== '') {
69
71
  configPath = ''
70
72
  }
71
73
 
72
- const { PicGo } = require('..')
73
- const picgo = new PicGo(configPath)
74
+ const picgo = await PicGo.create(configPath)
74
75
  const errorMessage = 'Upload failed, please check your network and config'
75
76
 
76
- handleResponse = ({
77
+ const handleResponse = ({
77
78
  response,
78
79
  statusCode = 200,
79
80
  header = {
@@ -89,43 +90,42 @@ handleResponse = ({
89
90
  if (body && body.success === false) {
90
91
  console.log('[PicList Server] upload failed, see log for more detail ↑')
91
92
  }
92
- response.writeHead(statusCode, header)
93
- response.write(JSON.stringify(body))
94
- response.end()
95
- }
96
-
97
- ensureHTTPLink = url => {
98
- return url.startsWith('http') ? url : `http://${url}`
93
+ try {
94
+ response.writeHead(statusCode, header)
95
+ response.end(JSON.stringify(body))
96
+ } catch (e) {
97
+ console.error('[PicList Server] response error:', e)
98
+ }
99
99
  }
100
100
 
101
101
  class Router {
102
102
  constructor() {
103
- this.router = new Map()
103
+ this.routes = new Map()
104
104
  }
105
105
 
106
- addRoute(method, url, callback, urlparams) {
107
- if (!this.router.has(url)) {
108
- this.router.set(url, new Map())
106
+ add(method, url, callback, urlparams) {
107
+ if (!this.routes.has(url)) {
108
+ this.routes.set(url, new Map())
109
109
  }
110
- this.router.get(url).set(method, { handler: callback, urlparams })
110
+ this.routes.get(url).set(method, { handler: callback, urlparams })
111
111
  }
112
112
 
113
113
  get(url, callback, urlparams) {
114
- this.addRoute('GET', url, callback, urlparams)
114
+ this.add('GET', url, callback, urlparams)
115
115
  }
116
116
 
117
117
  post(url, callback, urlparams) {
118
- this.addRoute('POST', url, callback, urlparams)
118
+ this.add('POST', url, callback, urlparams)
119
119
  }
120
120
 
121
121
  any(url, callback, urlparams) {
122
- this.addRoute('GET', url, callback, urlparams)
123
- this.addRoute('POST', url, callback, urlparams)
122
+ this.add('GET', url, callback, urlparams)
123
+ this.add('POST', url, callback, urlparams)
124
124
  }
125
125
 
126
126
  getHandler(url, method) {
127
- if (this.router.has(url)) {
128
- const methods = this.router.get(url)
127
+ if (this.routes.has(url)) {
128
+ const methods = this.routes.get(url)
129
129
  if (methods.has(method)) {
130
130
  return methods.get(method)
131
131
  }
@@ -134,16 +134,16 @@ class Router {
134
134
  }
135
135
  }
136
136
 
137
- let router = new Router()
137
+ const router = new Router()
138
138
 
139
139
  const multerStorage = multer.diskStorage({
140
- destination: function (_req, _file, cb) {
140
+ destination(_req, _file, cb) {
141
141
  if (!fs.existsSync(tempDir)) {
142
142
  fs.mkdirSync(tempDir)
143
143
  }
144
144
  cb(null, tempDir)
145
145
  },
146
- filename: function (_req, file, cb) {
146
+ filename(_req, file, cb) {
147
147
  if (!/[^\u0000-\u00ff]/.test(file.originalname)) {
148
148
  file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
149
149
  }
@@ -190,11 +190,13 @@ router.post('/upload', async ({ response, list = [], urlparams }) => {
190
190
  }
191
191
  })
192
192
  }
193
+
194
+ let needRestore = false
193
195
  let currentPicBedType = ''
194
196
  let currentPicBedConfig = {}
195
197
  let currentPicBedConfigId = ''
196
198
  const currentPicBed = picgo.getConfig('picBed') || {}
197
- let needRestore = false
199
+
198
200
  if (picbed) {
199
201
  currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms'
200
202
  currentPicBedConfig = currentPicBed[currentPicBedType] || {}
@@ -215,7 +217,7 @@ router.post('/upload', async ({ response, list = [], urlparams }) => {
215
217
  const picBeds = picgo.getConfig('uploader')
216
218
  const currentPicBedList = picBeds?.[picbed]?.configList
217
219
  if (currentPicBedList) {
218
- const currentConfig = currentPicBedList?.find((item) => item._configName === configName)
220
+ const currentConfig = currentPicBedList?.find(item => item._configName === configName)
219
221
  changeCurrentUploader(picbed, currentConfig, currentConfig._id)
220
222
  }
221
223
  }
@@ -292,9 +294,19 @@ router.post('/upload', async ({ response, list = [], urlparams }) => {
292
294
  }
293
295
  })
294
296
 
297
+ router.any('/heartbeat', async ({ response }) => {
298
+ handleResponse({
299
+ response,
300
+ body: {
301
+ success: true,
302
+ result: 'alive'
303
+ }
304
+ })
305
+ })
306
+
295
307
  async function responseForGet({ response }) {
296
308
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
297
- response.write(`
309
+ response.end(`
298
310
  <!DOCTYPE html>
299
311
  <html lang="en">
300
312
  <head>
@@ -352,21 +364,9 @@ async function responseForGet({ response }) {
352
364
  }</pre>
353
365
 
354
366
  </body>
355
- </html>
356
- `)
357
- response.end()
367
+ </html>`)
358
368
  }
359
369
 
360
- router.any('/heartbeat', async ({ response }) => {
361
- handleResponse({
362
- response,
363
- body: {
364
- success: true,
365
- result: 'alive'
366
- }
367
- })
368
- })
369
-
370
370
  class Server {
371
371
  constructor() {
372
372
  this.httpServer = http.createServer(this.handleRequest)
@@ -378,102 +378,106 @@ class Server {
378
378
  return
379
379
  }
380
380
 
381
- if (request.method === 'POST') {
382
- const [url, query] = (request.url || '').split('?')
383
- if (!router.getHandler(url, 'POST')) {
384
- console.log(`[PicList Server] don't support [${url}] endpoint`)
381
+ const rawUrl = request.url || ''
382
+ const [url, query] = rawUrl.split('?')
383
+ const method = request.method || 'GET'
384
+ const handlerEntry = router.getHandler(url, method)
385
+
386
+ if (!handlerEntry) {
387
+ console.log(`[PicList Server] don't support [${method}] ${url} endpoint`)
388
+ if (method === 'GET' || method === 'POST') {
385
389
  handleResponse({
386
390
  response,
387
- statusCode: 404,
388
- body: {
389
- success: false
390
- }
391
+ statusCode: method === 'POST' ? 404 : 404,
392
+ body: { success: false }
391
393
  })
392
394
  } else {
393
- let urlSP = query ? new URLSearchParams(query) : undefined
394
- console.log('[PicList Server] get the request from IP:', request.socket.remoteAddress)
395
- if (request.socket.remoteAddress === '127.0.0.1' || request.socket.remoteAddress === '::1') {
396
- if (urlSP) {
397
- urlSP.set('key', key)
398
- } else {
399
- urlSP = new URLSearchParams('key=' + key)
400
- }
401
- }
402
- if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) {
403
- uploadMulter.any()(request, response, err => {
404
- if (err) {
405
- console.log('[PicList Server]', err)
406
- return handleResponse({
407
- response,
408
- body: {
409
- success: false,
410
- message: 'Error processing formData'
411
- }
412
- })
413
- }
414
-
415
- const list = request.files.map(file => file.path)
395
+ response.statusCode = 405
396
+ response.end()
397
+ }
398
+ return
399
+ }
416
400
 
417
- const handler = router.getHandler(url, 'POST')?.handler
418
- if (handler) {
419
- handler({
420
- list: list,
421
- response,
422
- urlparams: urlSP
423
- })
424
- }
425
- })
401
+ if (method === 'POST') {
402
+ let urlSP = query ? new URLSearchParams(query) : undefined
403
+ const remote = request.socket?.remoteAddress
404
+ if (isLoopback(remote)) {
405
+ if (urlSP) {
406
+ urlSP.set('key', key)
426
407
  } else {
427
- let body = ''
428
- let postObj
429
- request.on('data', chunk => {
430
- body += chunk
431
- })
432
- request.on('end', () => {
433
- try {
434
- postObj = body === '' ? {} : JSON.parse(body)
435
- } catch (err) {
436
- console.log('[PicList Server]', err)
437
- return handleResponse({
438
- response,
439
- body: {
440
- success: false,
441
- message: 'Not sending data in JSON format'
442
- }
443
- })
444
- }
408
+ urlSP = new URLSearchParams('key=' + key)
409
+ }
410
+ }
411
+
412
+ const contentType = String(request.headers['content-type'] || '')
413
+ const isForm = contentType.startsWith('multipart/form-data')
414
+ if (isForm) {
415
+ uploadMulter.any()(request, response, err => {
416
+ if (err) {
417
+ console.log('[PicList Server]', err)
418
+ return handleResponse({
419
+ response,
420
+ body: {
421
+ success: false,
422
+ message: 'Error processing formData'
423
+ }
424
+ })
425
+ }
426
+ const list = request.files.map(file => file.path)
427
+ if (handlerEntry.handler) {
428
+ handlerEntry.handler({
429
+ list,
430
+ response,
431
+ urlparams: urlSP
432
+ })
433
+ }
434
+ })
435
+ } else {
436
+ let body = ''
437
+ let postObj
438
+ request.on('data', chunk => {
439
+ body += chunk
440
+ })
441
+ request.on('end', () => {
442
+ try {
443
+ postObj = body === '' ? {} : JSON.parse(body)
445
444
  console.log('[PicList Server] get the request', body)
446
- const handler = router.getHandler(url, 'POST')?.handler
447
- if (handler) {
448
- handler({
445
+ if (handlerEntry.handler) {
446
+ handlerEntry.handler({
449
447
  ...postObj,
450
448
  response,
451
449
  urlparams: urlSP
452
450
  })
453
451
  }
454
- })
455
- }
452
+ } catch (err) {
453
+ console.log('[PicList Server]', err)
454
+ return handleResponse({
455
+ response,
456
+ body: {
457
+ success: false,
458
+ message: 'Not sending data in JSON format'
459
+ }
460
+ })
461
+ }
462
+ })
456
463
  }
457
- } else if (request.method === 'GET') {
458
- const [url, query] = (request.url || '').split('?')
459
- if (!router.getHandler(url, 'GET')) {
460
- console.log(`[PicList Server] don't support [${url}] endpoint`)
461
- response.statusCode = 404
462
- response.end()
463
- } else {
464
- const handler = router.getHandler(url, 'GET')?.handler
465
- if (handler) {
466
- handler({
467
- response,
468
- urlparams: query ? new URLSearchParams(query) : undefined
469
- })
470
- }
464
+ return
465
+ }
466
+
467
+ if (request.method === 'GET') {
468
+ if (handlerEntry.handler) {
469
+ handlerEntry.handler({
470
+ response,
471
+ urlparams: query ? new URLSearchParams(query) : undefined
472
+ })
471
473
  }
472
- } else {
473
- console.log(`[PicList Server] don't support [${request.method}] method`)
474
- response.statusCode = 405
475
- response.end()
474
+
475
+ return
476
476
  }
477
+
478
+ console.log(`[PicList Server] don't support [${request.method}] method`)
479
+ response.statusCode = 405
480
+ response.end()
477
481
  }
478
482
 
479
483
  listen(port) {
@@ -481,10 +485,12 @@ class Server {
481
485
  if (typeof port === 'string') {
482
486
  port = parseInt(port, 10)
483
487
  }
488
+
484
489
  this.httpServer.listen(port, defaultHost).on('error', async err => {
485
490
  if (err.code === 'EADDRINUSE') {
486
491
  try {
487
- await axios.post(ensureHTTPLink(`${defaultHost}:${port}/heartbeat`))
492
+ const probeHost = defaultHost === '0.0.0.0' || defaultHost === '::' ? '127.0.0.1' : defaultHost
493
+ await axios.post(ensureHTTPLink(`${probeHost}:${port}/heartbeat`))
488
494
  console.log(`[PicList Server] server is already running at ${port}, no need to start again`)
489
495
  this.shutdown(true)
490
496
  } catch (e) {
@@ -504,9 +510,13 @@ class Server {
504
510
  }
505
511
 
506
512
  shutdown(hasStarted) {
507
- this.httpServer.close()
508
- if (!hasStarted) {
509
- console.log('[PicList Server] shutdown')
513
+ try {
514
+ this.httpServer.close()
515
+ if (!hasStarted) {
516
+ console.log('[PicList Server] shutdown')
517
+ }
518
+ } catch (error) {
519
+ console.error('[PicList Server] error shutting down', error)
510
520
  }
511
521
  }
512
522
 
@@ -519,4 +529,13 @@ class Server {
519
529
  const server = new Server()
520
530
  server.startup()
521
531
 
522
- module.exports = server
532
+ process.on('SIGINT', () => {
533
+ server.shutdown()
534
+ process.exit(0)
535
+ })
536
+ process.on('SIGTERM', () => {
537
+ server.shutdown()
538
+ process.exit(0)
539
+ })
540
+
541
+ export default server