Merge pull request #174 from JeremyYu-cn/generate-html

feat: format server import method: from commonJs to EsModule
This commit is contained in:
Jeremy Yu 2024-12-28 16:35:28 +08:00 committed by GitHub
commit b5b9fe385c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 440 additions and 403 deletions

View File

@ -19,6 +19,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/multiparty": "^4.2.1",
"@types/node": "^16.18.105", "@types/node": "^16.18.105",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"ts-loader": "^6.0.4", "ts-loader": "^6.0.4",
@ -247,6 +248,16 @@
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true "dev": true
}, },
"node_modules/@types/multiparty": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/@types/multiparty/-/multiparty-4.2.1.tgz",
"integrity": "sha512-Wi6aK3FgvHLvCDxD7ngG4w8MsCK9h64EB53Gvc8t7FVX81tleiz8vFS3ebBohGxqHRzNGHaNwhfdxTGOGAXm6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.18.105", "version": "16.18.105",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.105.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.105.tgz",
@ -4007,6 +4018,15 @@
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true "dev": true
}, },
"@types/multiparty": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/@types/multiparty/-/multiparty-4.2.1.tgz",
"integrity": "sha512-Wi6aK3FgvHLvCDxD7ngG4w8MsCK9h64EB53Gvc8t7FVX81tleiz8vFS3ebBohGxqHRzNGHaNwhfdxTGOGAXm6A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/node": { "@types/node": {
"version": "16.18.105", "version": "16.18.105",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.105.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-16.18.105.tgz",

View File

@ -27,6 +27,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/multiparty": "^4.2.1",
"@types/node": "^16.18.105", "@types/node": "^16.18.105",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"ts-loader": "^6.0.4", "ts-loader": "^6.0.4",

View File

@ -17,34 +17,34 @@ const serviceComfig = {
/** /**
* *
*/ */
exports.servicePort = serviceComfig.port export const servicePort = serviceComfig.port
/** /**
* *
*/ */
exports.drawLink = isDev ? 'http://127.0.0.1:5173/draw' : serviceComfig.website + '/draw' export const drawLink = isDev ? 'http://127.0.0.1:5173/draw' : serviceComfig.website + '/draw'
/** /**
* *
*/ */
exports.filePath = isDev ? process.cwd() + `/static/` : serviceComfig.filePath export const filePath = isDev ? process.cwd() + `/static/` : serviceComfig.filePath
/** /**
* chrome浏览器位置 * chrome浏览器位置
*/ */
exports.executablePath = isDev ? null : '/opt/google/chrome-unstable/chrome' export const executablePath = isDev ? null : '/opt/google/chrome-unstable/chrome'
/** /**
* *
*/ */
exports.maxNum = 2 export const maxNum = 2
/** /**
* *
*/ */
exports.upperLimit = 20 export const upperLimit = 20
/** /**
* *
*/ */
exports.releaseTime = 300 export const releaseTime = 300

View File

@ -7,7 +7,7 @@
*/ */
let path = '/api' let path = '/api'
module.exports = { export default {
SCREENGHOT: path + '/screenshots', SCREENGHOT: path + '/screenshots',
PRINTSCREEN: path + '/printscreen', PRINTSCREEN: path + '/printscreen',
// 后端示例 // 后端示例

View File

@ -5,12 +5,12 @@
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-08-12 13:40:13 * @LastEditTime: 2024-08-12 13:40:13
*/ */
const rExpress = require('express') import rExpress from 'express'
const screenshots = require('../service/screenshots.ts') import screenshots from '../service/screenshots'
const fileService = require('../service/files.ts') import fileService from '../service/files'
const userService = require('../service/user.ts') import userService from '../service/user'
const designService = require('../service/design.ts') import designService from '../service/design'
const api = require('./api.ts') import api from './api'
const rRouter = rExpress.Router() const rRouter = rExpress.Router()
rRouter.get(api.SCREENGHOT, screenshots.screenshots) rRouter.get(api.SCREENGHOT, screenshots.screenshots)
@ -23,4 +23,4 @@ rRouter.get(api.GET_MATERIAL, designService.getMaterial)
rRouter.get(api.GET_PHOTOS, designService.getPhotos) rRouter.get(api.GET_PHOTOS, designService.getPhotos)
rRouter.post(api.UPDATE_TEMPLATE, designService.saveTemplate) rRouter.post(api.UPDATE_TEMPLATE, designService.saveTemplate)
module.exports = rRouter export default rRouter

View File

@ -5,13 +5,13 @@
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-11-14 17:36:17 * @LastEditTime: 2024-11-14 17:36:17
*/ */
const express = require('express')
const bodyParser = require('body-parser') import express from 'express'
const fs = require('fs') import bodyParser from 'body-parser'
// const path = require('path') import fs from 'fs'
const router = require('./control/router.ts') import router from './control/router'
const { filePath, servicePort } = require('./configs.ts') import { filePath, servicePort } from './configs'
const handleTimeout = require('./utils/timeout.ts') import handleTimeout from './utils/timeout'
const port = process.env.PORT || servicePort const port = process.env.PORT || servicePort
const app = express() const app = express()

View File

@ -6,111 +6,120 @@
* @LastEditTime: 2024-08-17 11:22:42 * @LastEditTime: 2024-08-17 11:22:42
*/ */
import { Request, Response } from 'express' import { Request, Response } from 'express'
const fs = require('fs') import fs from 'fs'
const path = require('path') import path from 'path'
const axios = require('../utils/http.ts') import axios from '../utils/http'
const multiparty = require('multiparty') import multiparty from 'multiparty'
const { filePath } = require('../configs.ts') import { filePath } from '../configs'
const { checkCreateFolder, randomCode, send } = require('../utils/tools.ts') import { checkCreateFolder, randomCode, send } from '../utils/tools'
const FileUrl = 'http://localhost:7001/static/' const FileUrl = 'http://localhost:7001/static/'
module.exports = {
// design/list 获取模板列表(虚拟) // design/list 获取模板列表(虚拟)
async getTemplates(req: any, res: Response) { export async function getTemplates(req: any, res: Response) {
/** /**
* @api {get} /design/list * @api {get} /design/list
* @apiVersion 1.0.0 * @apiVersion 1.0.0
* @apiGroup design * @apiGroup design
*/ */
const { cate, type } = req.query const { cate, type } = req.query
const tempPath = type == 1 ? `../mock/components/list/${cate}.json` : '../mock/templates/list.json' const tempPath = type == 1 ? `../mock/components/list/${cate}.json` : '../mock/templates/list.json'
try { try {
const list = fs.readFileSync(path.resolve(__dirname, tempPath), 'utf8') const list = fs.readFileSync(path.resolve(__dirname, tempPath), 'utf8')
send.success(res, { list: JSON.parse(list) }) send.success(res, { list: JSON.parse(list) })
} catch (error) {} } catch (error) {}
},
// design/temp 获取模板(虚拟)
async getDetail(req: any, res: Response) {
/**
* @api {get} /design/list
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate, type, id } = req.query
const dPath = type == 1 ? `../mock/components/detail/${id}.json` : `../mock/templates/${id}.json`
try {
const detail = fs.readFileSync(path.resolve(__dirname, dPath), 'utf8')
send.success(res, JSON.parse(detail))
} catch (error) {}
},
// design/material 获取素材(虚拟)
async getMaterial(req: any, res: any) {
/**
* @api {get} /design/material
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate } = req.query
try {
const detail = fs.readFileSync(path.resolve(__dirname, `../mock/materials/${cate}.json`), 'utf8')
send.success(res, { list: JSON.parse(detail) })
} catch (error) {
console.log(error)
}
},
// design/imgs 获取照片素材(虚拟)
async getPhotos(req: any, res: any) {
/**
* @api {get} /design/imgs
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate } = req.query
try {
const detail = fs.readFileSync(path.resolve(__dirname, `../mock/materials/photos/${cate}.json`), 'utf8')
send.success(res, { list: JSON.parse(detail) })
} catch (error) {}
},
// design/edit 保存模板(虚拟)
async saveTemplate(req: any, res: any) {
/**
* @api {post} /design/edit
* @apiVersion 1.0.0
* @apiGroup design
*/
let { id, title, data, width, height, type, cate, tag } = req.body
const folder = type == 1 ? 'components/detail' : 'templates'
const listPath = type == 1 ? 'components/list/comp.json' : 'templates/list.json'
try {
const isAdd = !id // 是否新增模板
id = id || randomCode(8)
const savePath = path.resolve(__dirname, `../mock/${folder}/${id}.json`)
const jsonData = {
id,
data,
title,
width,
height,
}
fs.writeFileSync(savePath, JSON.stringify(jsonData))
// 生成封面
const size = width > height ? 640 : 320
const fetchScreenshotUrl = `http://localhost:7001/api/screenshots?tempid=${id}&tempType=${type}&width=${width}&height=${height}&type=cover&size=${size}&quality=75`
await axios.get(fetchScreenshotUrl, { responseType: 'arraybuffer' })
// 保存到其他地方可以设置 responseType: 'arraybuffer' 后操作buffer这里只为了得到封面发起请求就可以了
if (isAdd) {
const listVal = fs.readFileSync(path.resolve(__dirname, `../mock/${listPath}`), 'utf8')
const list = JSON.parse(listVal)
const cover = type == 1 ? FileUrl + `/${id}-screenshot.png` : FileUrl + `/${id}-cover.jpg`
list.unshift({ id, cover, title, width, height })
fs.writeFileSync(path.resolve(__dirname, `../mock/${listPath}`), JSON.stringify(list))
}
send.success(res, { id })
} catch (error) {
console.log(error)
}
},
} }
export {} // design/temp 获取模板(虚拟)
export async function getDetail(req: any, res: Response) {
/**
* @api {get} /design/list
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate, type, id } = req.query
const dPath = type == 1 ? `../mock/components/detail/${id}.json` : `../mock/templates/${id}.json`
try {
const detail = fs.readFileSync(path.resolve(__dirname, dPath), 'utf8')
send.success(res, JSON.parse(detail))
} catch (error) {}
}
// design/material 获取素材(虚拟)
export async function getMaterial(req: any, res: any) {
/**
* @api {get} /design/material
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate } = req.query
try {
const detail = fs.readFileSync(path.resolve(__dirname, `../mock/materials/${cate}.json`), 'utf8')
send.success(res, { list: JSON.parse(detail) })
} catch (error) {
console.log(error)
}
}
// design/imgs 获取照片素材(虚拟)
export async function getPhotos(req: any, res: any) {
/**
* @api {get} /design/imgs
* @apiVersion 1.0.0
* @apiGroup design
*/
const { cate } = req.query
try {
const detail = fs.readFileSync(path.resolve(__dirname, `../mock/materials/photos/${cate}.json`), 'utf8')
send.success(res, { list: JSON.parse(detail) })
} catch (error) {}
}
// design/edit 保存模板(虚拟)
export async function saveTemplate(req: any, res: any) {
/**
* @api {post} /design/edit
* @apiVersion 1.0.0
* @apiGroup design
*/
let { id, title, data, width, height, type, cate, tag } = req.body
const folder = type == 1 ? 'components/detail' : 'templates'
const listPath = type == 1 ? 'components/list/comp.json' : 'templates/list.json'
try {
const isAdd = !id // 是否新增模板
id = id || randomCode(8)
const savePath = path.resolve(__dirname, `../mock/${folder}/${id}.json`)
const jsonData = {
id,
data,
title,
width,
height,
}
fs.writeFileSync(savePath, JSON.stringify(jsonData))
// 生成封面
const size = width > height ? 640 : 320
const fetchScreenshotUrl = `http://localhost:7001/api/screenshots?tempid=${id}&tempType=${type}&width=${width}&height=${height}&type=cover&size=${size}&quality=75`
await axios.get(fetchScreenshotUrl, { responseType: 'arraybuffer' })
// 保存到其他地方可以设置 responseType: 'arraybuffer' 后操作buffer这里只为了得到封面发起请求就可以了
if (isAdd) {
const listVal = fs.readFileSync(path.resolve(__dirname, `../mock/${listPath}`), 'utf8')
const list = JSON.parse(listVal)
const cover = type == 1 ? FileUrl + `/${id}-screenshot.png` : FileUrl + `/${id}-cover.jpg`
list.unshift({ id, cover, title, width, height })
fs.writeFileSync(path.resolve(__dirname, `../mock/${listPath}`), JSON.stringify(list))
}
send.success(res, { id })
} catch (error) {
console.log(error)
}
}
export default {
getTemplates,
getDetail,
getMaterial,
getPhotos,
saveTemplate
}

View File

@ -12,49 +12,48 @@ const { checkCreateFolder, randomCode, copyFile, send } = require('../utils/tool
const FileUrl = 'http://localhost:7001/static/' const FileUrl = 'http://localhost:7001/static/'
module.exports = {
// api/file/upload 上传接口 // api/file/upload 上传接口
async upload(req: Request, res: Response) { export async function upload(req: Request, res: Response) {
/** /**
* @api {post} /api/file/upload * @api {post} /api/file/upload
* @apiVersion 1.0.0 * @apiVersion 1.0.0
* @apiGroup file * @apiGroup file
* *
* @apiParam {File} file * @apiParam {File} file
* @apiParam {String} folder * @apiParam {String} folder
* @apiParam {String} name * @apiParam {String} name
* *
* @apiSuccess (__组__) {__类型__} __字段名__ __返回字段说明__ * @apiSuccess (__组__) {__类型__} __字段名__ __返回字段说明__
*/ */
const form = new multiparty.Form() const form = new multiparty.Form()
form.parse(req, async function (err: any, fields: any, files: any) { form.parse(req, async function (err: any, fields: any, files: any) {
if (err) { if (err) {
console.error('上传文件出错!') console.error('上传文件出错!')
return return
} }
if (files) { if (files) {
const file = files.file ? files.file[0] : {} const file = files.file ? files.file[0] : {}
const { size, headers, originalFilename } = file const { size, headers, originalFilename } = file
const fileType = headers['content-type'].split('/')[1] const fileType = headers['content-type'].split('/')[1]
const Suffix = originalFilename.split('.').pop() || fileType || 'png' const Suffix = originalFilename.split('.').pop() || fileType || 'png'
const { folder = '', name = `${randomCode(12)}.${Suffix}` } = fields const { folder = '', name = `${randomCode(12)}.${Suffix}` } = fields
const folderPath = `${filePath}${folder ? `${folder}/` : ''}` const folderPath = `${filePath}${folder ? `${folder}/` : ''}`
checkCreateFolder(folderPath) // 检测对应目录是否存在 checkCreateFolder(folderPath) // 检测对应目录是否存在
const targetPath = `${folderPath}${name}` const targetPath = `${folderPath}${name}`
copyFile(file.path, targetPath) copyFile(file.path, targetPath)
.then(() => { .then(() => {
const url = `${FileUrl}${folder ? folder + '/' : ''}${name}` const url = `${FileUrl}${folder ? folder + '/' : ''}${name}`
send.success(res, { send.success(res, {
key: `${folder}/${name}`, key: `${folder}/${name}`,
url, url,
})
}) })
.catch((err: any) => { })
console.log('上传异常', err) .catch((err: any) => {
}) console.log('上传异常', err)
} })
}) }
}, })
} }
export {} export default { upload }

View File

@ -5,106 +5,105 @@
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-08-17 11:23:58 * @LastEditTime: 2024-08-17 11:23:58
*/ */
const { saveScreenshot } = require('../utils/download-single.ts') import { saveScreenshot } from '../utils/download-single'
const uuid = require('../utils/uuid.ts') import uuid from '../utils/uuid'
const { filePath, upperLimit, drawLink } = require('../configs.ts') import { filePath, upperLimit, drawLink } from '../configs'
const { queueRun, queueList } = require('../utils/node-queue.ts') import { queueRun, queueList } from '../utils/node-queue'
// const path = require('path') // const path = require('path')
const fs = require('fs')
module.exports = { /**
async screenshots(req: any, res: any) { * @api {get} api/screenshots
/** * @apiVersion 1.0.0
* @api {get} api/screenshots * @apiGroup screenShot
* @apiVersion 1.0.0 *
* @apiGroup screenShot * @apiParam {String|Number} id () id
* * @apiParam {String|Number} tempid () idid时取该值
* @apiParam {String|Number} id () id * @apiParam {String|Number} tempType ()
* @apiParam {String|Number} tempid () idid时取该值 * @apiParam {String} width ()
* @apiParam {String|Number} tempType () * @apiParam {String} height ()
* @apiParam {String} width () * @apiParam {String} screenshot_url
* @apiParam {String} height () * @apiParam {String} type , file正常截图返回cover封面生成file
* @apiParam {String} screenshot_url * @apiParam {String} size ,
* @apiParam {String} type , file正常截图返回cover封面生成file * @apiParam {String} quality ,
* @apiParam {String} size , * @apiParam {String|Number} index ,
* @apiParam {String} quality , */
* @apiParam {String|Number} index , export async function screenshots(req: any, res: any) {
*/ let { id, tempid, tempType, width, height, screenshot_url, type = 'file', size, quality, index = 0 } = req.query
let { id, tempid, tempType, width, height, screenshot_url, type = 'file', size, quality, index = 0 } = req.query id == 'undefined' && (id = null)
id == 'undefined' && (id = null) const url = (screenshot_url || drawLink) + `${id ? '?id=' : '?tempid='}`
const url = (screenshot_url || drawLink) + `${id ? '?id=' : '?tempid='}` id = id || tempid
id = id || tempid const path = filePath + `${id}-screenshot.png`
const path = filePath + `${id}-screenshot.png` const thumbPath = type === 'cover' && tempType != 1 ? filePath + `${id}-cover.jpg` : null
const thumbPath = type === 'cover' && tempType != 1 ? filePath + `${id}-cover.jpg` : null
if (id && width && height) { if (id && width && height) {
if (queueList.length > upperLimit) { if (queueList.length > upperLimit) {
res.json({ code: 200, msg: '服务器表示顶不住啊,等等再来吧~' }) res.json({ code: 200, msg: '服务器表示顶不住啊,等等再来吧~' })
return return
}
const targetUrl = url + id + `${tempType ? '&tempType=' + tempType : ''}` + `&index=${index}`
queueRun(saveScreenshot, targetUrl, { width, height, path, thumbPath, size, quality })
.then(() => {
res.setHeader('Content-Type', 'image/jpg')
// const stats = fs.statSync(path)
// res.setHeader('Cache-Control', stats.size)
type === 'file' ? res.sendFile(path) : res.sendFile(thumbPath)
})
.catch((e: any) => {
res.json({ code: 500, msg: '图片生成错误' })
})
} else {
res.json({ code: 500, msg: '缺少参数,请检查' })
} }
}, const targetUrl = url + id + `${tempType ? '&tempType=' + tempType : ''}` + `&index=${index}`
async printscreen(req: any, res: any) { queueRun(saveScreenshot, targetUrl, { width, height, path, thumbPath, size, quality })
/** .then(() => {
* @api {get} api/printscreen res.setHeader('Content-Type', 'image/jpg')
* @apiVersion 1.0.0 // const stats = fs.statSync(path)
* @apiGroup screenShot // res.setHeader('Cache-Control', stats.size)
* type === 'file' ? res.sendFile(path) : res.sendFile(thumbPath)
* @apiParam {String} url () link })
* @apiParam {String} width () .catch((e: any) => {
* @apiParam {String} height () res.json({ code: 500, msg: '图片生成错误' })
* @apiParam {Boolean} prevent (, false) true: 使 })
* @apiParam {String} type (, file) file: 返回二进制文件cover: 立即返回地址(path, thumbPath) } else {
* @apiParam {String} size (type=cover生效, eg:300300jpeg res.json({ code: 500, msg: '缺少参数,请检查' })
* @apiParam {String} quality (size生效, eg:75)压缩质量:1-100 }
* @apiParam {Number} wait () ms
* @apiParam {String} ua () eg: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
* @apiParam {String} devices () uawidthheight均会失效eg: iPhone 6 /src/utils/widget/Device.js
* @apiParam {Number} scale () (DPR) 1~41
*/
let { width = 375, height = 0, url, type = 'file', size, quality, prevent = false, ua, devices, scale, wait } = req.query
const path = filePath + `screenshot_${new Date().getTime()}_${uuid()}.png`
const thumbPath = type === 'cover' ? path.replace('.png', '.jpg') : null
if (url) {
const sign = `${new Date().getTime()}_${uuid(8)}`
req._queueSign = sign
// console.log(url + id, path, thumbPath);
if (queueList.length > upperLimit) {
res.json({ code: 200, msg: '业务繁忙,等等再来吧~' })
return
}
queueRun(saveScreenshot, url, { width, height, path, thumbPath, size, quality, prevent, ua, devices, scale, wait }, sign)
.then(() => {
if (!res.headersSent) {
// res.setHeader('Content-Type', 'image/jpg')
// const stats = fs.statSync(path)
// res.setHeader('Cache-Control', stats.size)
res.json({ code: 200, msg: '截图成功', data: { path, thumbPath } })
} else {
res.json({ code: 200, msg: 'ok' })
}
})
.catch((e: any) => {
res.json({ code: 500, msg: '图片生成错误!' })
})
} else {
res.json({ code: 500, msg: '缺少参数,请检查' })
}
},
} }
export {} /**
* @api {get} api/printscreen
* @apiVersion 1.0.0
* @apiGroup screenShot
*
* @apiParam {String} url () link
* @apiParam {String} width ()
* @apiParam {String} height ()
* @apiParam {Boolean} prevent (, false) true: 使
* @apiParam {String} type (, file) file: 返回二进制文件cover: 立即返回地址(path, thumbPath)
* @apiParam {String} size (type=cover生效, eg:300300jpeg
* @apiParam {String} quality (size生效, eg:75)压缩质量:1-100
* @apiParam {Number} wait () ms
* @apiParam {String} ua () eg: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
* @apiParam {String} devices () uawidthheight均会失效eg: iPhone 6 /src/utils/widget/Device.js
* @apiParam {Number} scale () (DPR) 1~41
*/
export async function printscreen(req: any, res: any) {
let { width = 375, height = 0, url, type = 'file', size, quality, prevent = false, ua, devices, scale, wait } = req.query
const path = filePath + `screenshot_${new Date().getTime()}_${uuid()}.png`
const thumbPath = type === 'cover' ? path.replace('.png', '.jpg') : null
if (url) {
const sign = `${new Date().getTime()}_${uuid()}`
req._queueSign = sign
// console.log(url + id, path, thumbPath);
if (queueList.length > upperLimit) {
res.json({ code: 200, msg: '业务繁忙,等等再来吧~' })
return
}
queueRun(saveScreenshot, url, { width, height, path, thumbPath, size, quality, prevent, ua, devices, scale, wait }, sign)
.then(() => {
if (!res.headersSent) {
// res.setHeader('Content-Type', 'image/jpg')
// const stats = fs.statSync(path)
// res.setHeader('Cache-Control', stats.size)
res.json({ code: 200, msg: '截图成功', data: { path, thumbPath } })
} else {
res.json({ code: 200, msg: 'ok' })
}
})
.catch((e: any) => {
res.json({ code: 500, msg: '图片生成错误!' })
})
} else {
res.json({ code: 500, msg: '缺少参数,请检查' })
}
}
export default { printscreen, screenshots }

View File

@ -12,7 +12,7 @@ const { checkCreateFolder, filesReader, send } = require('../utils/tools.ts')
const FileUrl = 'http://localhost:7001/static/' const FileUrl = 'http://localhost:7001/static/'
module.exports = { export default {
// design/user/image 获取用户上传列表(虚拟) // design/user/image 获取用户上传列表(虚拟)
async getUserImages(req: Request, res: Response) { async getUserImages(req: Request, res: Response) {
/** /**
@ -24,5 +24,3 @@ module.exports = {
send.success(res, { list }) send.success(res, { list })
}, },
} }
export {}

View File

@ -15,7 +15,7 @@ const forceTimeOut = 60 // 强制超时时间,单位:秒
const maxPXs = 4211840 // 超出此规格会触发限制器降低dpr节省服务器资源 const maxPXs = 4211840 // 超出此规格会触发限制器降低dpr节省服务器资源
const maximum = 5000 // 最大宽高限制,超过截断以防止服务崩溃 const maximum = 5000 // 最大宽高限制,超过截断以防止服务崩溃
const saveScreenshot = async (url: string, { path, width, height, thumbPath, size = 0, quality = 0, prevent, ua, devices, scale, wait }: any) => { export const saveScreenshot = async (url: string, { path, width, height, thumbPath, size = 0, quality = 0, prevent, ua, devices, scale, wait }: any) => {
return new Promise(async (resolve: Function, reject: Function) => { return new Promise(async (resolve: Function, reject: Function) => {
let isPageLoad = false let isPageLoad = false
let browser: any = null let browser: any = null
@ -154,6 +154,5 @@ const saveScreenshot = async (url: string, { path, width, height, thumbPath, siz
}) })
} }
module.exports = { saveScreenshot } export default { saveScreenshot }
export {}

View File

@ -13,7 +13,7 @@ const forceTimeOut = 60 // 强制超时时间,单位:秒
let browser: typeof puppeteer = null let browser: typeof puppeteer = null
let release: any = null let release: any = null
const saveScreenshot = async (url: string, { path, width, height, thumbPath, size = 0, quality = 0, prevent, ua, devices, scale, wait }: any) => { export const saveScreenshot = async (url: string, { path, width, height, thumbPath, size = 0, quality = 0, prevent, ua, devices, scale, wait }: any) => {
return new Promise(async (resolve: Function) => { return new Promise(async (resolve: Function) => {
// 启动浏览器 // 启动浏览器
if (!browser) { if (!browser) {
@ -123,6 +123,4 @@ async function autoScroll(page: any) {
}) })
} }
module.exports = { saveScreenshot } export default { saveScreenshot }
export {}

View File

@ -6,73 +6,77 @@
* @LastEditTime: 2024-08-12 06:29:58 * @LastEditTime: 2024-08-12 06:29:58
*/ */
import fs from 'fs' import fs from 'fs'
const path = require('path') import path from 'path'
const imageSize = require('image-size') import imageSize from 'image-size'
const { filePath: StaticPath } = require('../configs.ts') import { filePath as StaticPath } from '../configs'
const FileUrl = 'http://localhost:7001/static/' const FileUrl = 'http://localhost:7001/static/'
module.exports = { export function copyFile(sourceFile: string, destinationFile: string): Promise<void> {
copyFile(sourceFile: string, destinationFile: string): Promise<void> { return new Promise((resolve, reject) => {
return new Promise((resolve, reject) => { const readStream = fs.createReadStream(sourceFile)
const readStream = fs.createReadStream(sourceFile) const writeStream = fs.createWriteStream(destinationFile)
const writeStream = fs.createWriteStream(destinationFile)
readStream.on('error', (err: any) => { readStream.on('error', (err: any) => {
reject(err) reject(err)
})
writeStream.on('error', (err: any) => {
reject(err)
})
writeStream.on('finish', () => {
resolve()
})
readStream.pipe(writeStream)
}) })
},
// 读取目录 writeStream.on('error', (err: any) => {
filesReader(directoryPath: string) { reject(err)
return new Promise((resolve) => {
try {
const files = fs.readdirSync(StaticPath + directoryPath)
const filesArray: any = []
files.forEach((file) => {
const filePath = path.join(directoryPath, file)
// const stats = fs.statSync(filePath);
const { width, height } = imageSize(StaticPath + filePath)
if (file !== '.DS_Store') {
const fileInfo = {
width,
height,
// filename: file,
// link: FileUrl + directoryPath,
url: `${FileUrl + directoryPath}/${file}`,
// filepath: StaticPath + filePath
// size: stats.size, // 文件大小
// modified: stats.mtime // 最后修改时间
}
filesArray.push(fileInfo)
}
})
// JSON.stringify(filesArray, null, 2)
resolve(filesArray)
} catch (err) {
console.error('Error reading directory:', err)
}
}) })
},
// 读取文件 writeStream.on('finish', () => {
readFile(directoryPath: string) { resolve()
return new Promise((resolve) => {
try {
resolve(fs.readFileSync(StaticPath + directoryPath, 'utf8'))
} catch (err) {
console.error('Error reading file:', err)
}
}) })
},
readStream.pipe(writeStream)
})
} }
export {} // 读取目录
export function filesReader(directoryPath: string) {
return new Promise((resolve) => {
try {
const files = fs.readdirSync(StaticPath + directoryPath)
const filesArray: any = []
files.forEach((file) => {
const filePath = path.join(directoryPath, file)
// const stats = fs.statSync(filePath);
const { width, height } = imageSize(StaticPath + filePath)
if (file !== '.DS_Store') {
const fileInfo = {
width,
height,
// filename: file,
// link: FileUrl + directoryPath,
url: `${FileUrl + directoryPath}/${file}`,
// filepath: StaticPath + filePath
// size: stats.size, // 文件大小
// modified: stats.mtime // 最后修改时间
}
filesArray.push(fileInfo)
}
})
// JSON.stringify(filesArray, null, 2)
resolve(filesArray)
} catch (err) {
console.error('Error reading directory:', err)
}
})
}
// 读取文件
export function readFile(directoryPath: string) {
return new Promise((resolve) => {
try {
resolve(fs.readFileSync(StaticPath + directoryPath, 'utf8'))
} catch (err) {
console.error('Error reading file:', err)
}
})
}
export default {
copyFile,
filesReader,
readFile,
}

View File

@ -5,7 +5,7 @@
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-08-12 13:59:34 * @LastEditTime: 2024-08-12 13:59:34
*/ */
const axios = require('axios') import axios from 'axios'
const httpRequest = axios.create({ const httpRequest = axios.create({
maxContentLength: Infinity, maxContentLength: Infinity,
@ -16,4 +16,4 @@ httpRequest.interceptors.response.use((config: any) => {
return config.data return config.data
}) })
module.exports = httpRequest export default httpRequest

View File

@ -10,7 +10,7 @@ interface Queue {
sign?: string | number sign?: string | number
} }
const { maxNum } = require('../configs.ts') import { maxNum } from '../configs'
const queueList: any = [] // 任务队列 const queueList: any = [] // 任务队列
let curNum = 0 // 当前执行的任务数 let curNum = 0 // 当前执行的任务数
@ -38,4 +38,4 @@ function run(Fn: Function) {
}) })
} }
module.exports = { queueRun, queueList } export { queueRun, queueList }

View File

@ -6,7 +6,7 @@
* @LastEditTime: 2023-07-05 20:17:00 * @LastEditTime: 2023-07-05 20:17:00
*/ */
module.exports = async (req: any, res: any, next: any) => { export default async (req: any, res: any, next: any) => {
const { queueList } = require('../utils/node-queue.ts') const { queueList } = require('../utils/node-queue.ts')
const time = 30000 // 设置所有HTTP请求的服务器响应超时时间 const time = 30000 // 设置所有HTTP请求的服务器响应超时时间
res.setTimeout(time, () => { res.setTimeout(time, () => {

View File

@ -5,71 +5,76 @@
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2024-08-12 13:48:40 * @LastEditTime: 2024-08-12 13:48:40
*/ */
const fs = require('fs') import fs from 'fs'
const path = require('path') import path from 'path'
const fsFunc = require('./fs.ts') import { filesReader, copyFile, readFile } from './fs'
module.exports = { export const send = {
send: { success: (res: any, result: any, msg: string = 'ok') => {
success: (res: any, result: any, msg: string = 'ok') => { res.json({
res.json({ code: 200,
code: 200, msg,
msg, result: result || undefined,
result: result || undefined, })
})
},
error: (res: any, msg: string = 'error') => {
res.json({
code: 400,
msg,
})
},
}, },
isNumber: (value: any) => { error: (res: any, msg: string = 'error') => {
return typeof value === 'number' && !isNaN(value) res.json({
code: 400,
msg,
})
}, },
buildTree: (data: any[]) => {}, }
groupBy: (array: any[], property: any) => {}, export const isNumber = (value: any) => {
// 检测目录并创建目录(支持深层级) return typeof value === 'number' && !isNaN(value)
checkCreateFolder: (folder: string) => { }
try {
const pathArr = splitPath(folder) export const buildTree = (data: any[]) => {}
let _path = ''
for (let i = 0; i < pathArr.length; i++) { export const groupBy = (array: any[], property: any) => {}
if (pathArr[i]) {
_path += `/${pathArr[i]}` // 检测目录并创建目录(支持深层级)
!fs.existsSync(_path) && fs.mkdirSync(_path) export const checkCreateFolder = (folder: string) => {
} try {
const pathArr = splitPath(folder)
let _path = ''
for (let i = 0; i < pathArr.length; i++) {
if (pathArr[i]) {
_path += `/${pathArr[i]}`
!fs.existsSync(_path) && fs.mkdirSync(_path)
} }
} catch (e) {} }
}, } catch (e) {}
// 检测文件 }
checkCreateFile: (filePath: string) => {
try { // 检测文件
if (!fs.existsSync(filePath)) { export const checkCreateFile = (filePath: string) => {
fs.writeFileSync(filePath, '') try {
} if (!fs.existsSync(filePath)) {
} catch (e) {
fs.writeFileSync(filePath, '') fs.writeFileSync(filePath, '')
} }
}, } catch (e) {
// 生成随机码 fs.writeFileSync(filePath, '')
randomCode: (length = 5) => { }
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * chars.length)
result += chars[randomIndex]
}
return result
},
// 取数组差集
findDifference: (a: any, b: any) => {
return a.concat(b).filter((v: any) => !a.includes(v) || !b.includes(v))
},
...fsFunc,
} }
// 生成随机码
export const randomCode = (length = 5) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * chars.length)
result += chars[randomIndex]
}
return result
}
// 取数组差集
export const findDifference = (a: any, b: any) => {
return a.concat(b).filter((v: any) => !a.includes(v) || !b.includes(v))
}
export { copyFile, readFile, filesReader }
/** /**
* *
* @param dirPath * @param dirPath
@ -81,4 +86,3 @@ function splitPath(dirPath: string) {
return normalizedPath.split(separator) return normalizedPath.split(separator)
} }
export {}

View File

@ -7,9 +7,9 @@
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
const nodeCrypto = require('crypto'); import nodeCrypto from 'crypto';
module.exports = () => export default () =>
// @ts-ignore // @ts-ignore
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c: number) => ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c: number) =>
(c ^ (nodeCrypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) (c ^ (nodeCrypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16)

View File

@ -3,7 +3,7 @@
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "target": "ESNEXT", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": ["dom", "es2015"], /* Specify library files to be included in the compilation. */ // "lib": ["dom", "es2015"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */ // "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */ // "checkJs": true, /* Report errors in .js files. */

View File

@ -39,6 +39,12 @@ module.exports = {
}, },
], ],
}, },
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'], // 解析的文件类型
alias: {
'@': path.resolve(__dirname, 'src') // 配置路径别名,指向 src 目录
}
},
// plugins: [new BundleAnalyzerPlugin()], // plugins: [new BundleAnalyzerPlugin()],
plugins: [ new buildPlugin() ] plugins: [ new buildPlugin() ]
} }