fix: 工具广场统一18000端口 + 去除电量限制 + bot头像更新
nginx反代(tools-nginx.conf): - 10个工具统一通过18000端口路径分发 - 解决/static/冲突(Lama精确匹配,其余给LibreTranslate) - 解决/api/冲突(PDF用sub_filter改为/pdf-api/,/api/给imgcompress) - Overleaf兜底处理所有未匹配的绝对路径 - 前端工具卡片统一走18000端口+路径 后端: - 去除对话电量限制(validateUser中的num扣减逻辑) 前端: - bot头像更新为战知logo(蓝底白字) - LaTeX公式编辑器"复制为图片"改为"下载为图片"(解决跨域问题) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,14 +51,6 @@ public class GptServiceImpl implements IGptService {
|
|||||||
if (ValidatorUtil.isNull(user)) {
|
if (ValidatorUtil.isNull(user)) {
|
||||||
throw new ProhibitVisitException();
|
throw new ProhibitVisitException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.getNum() < 1) {
|
|
||||||
throw new BusinessException("电量不足,请分享好友获取电量或开通会员");
|
|
||||||
}
|
|
||||||
// 扣电量
|
|
||||||
UpdateWrapper<User> uw = new UpdateWrapper<>();
|
|
||||||
uw.lambda().set(User::getNum, user.getNum() - 1).eq(BaseEntity::getId, user.getId());
|
|
||||||
userMapper.update(null, uw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -59,38 +59,45 @@ const toolCategories = reactive<Category[]>([
|
|||||||
{
|
{
|
||||||
name: '📄 文档处理',
|
name: '📄 文档处理',
|
||||||
tools: [
|
tools: [
|
||||||
{ id: 'stirling-pdf', name: 'Stirling PDF', icon: '', logoImg: stirlingLogo, description: 'PDF 合并、拆分、压缩、转换、加水印、OCR 识别等 30+ 功能', port: 18080, enabled: true, type: 'newtab' },
|
{ id: 'stirling-pdf', name: 'Stirling PDF', icon: '', logoImg: stirlingLogo, description: 'PDF 合并、拆分、压缩、转换、加水印、OCR 识别等 30+ 功能', path: '/spdf/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'trwebocr', name: '图片转文字 OCR', icon: '', logoImg: trwebocrLogo, description: '中文离线 OCR,识别图片中的文字和表格', port: 18083, enabled: true, type: 'newtab' },
|
{ id: 'trwebocr', name: '图片转文字 OCR', icon: '', logoImg: trwebocrLogo, description: '中文离线 OCR,识别图片中的文字和表格', path: '/ocr/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'libretranslate', name: '智能翻译', icon: '', logoImg: libretranslateLogo, description: '中英互译,支持文本和文件翻译,数据不出内网', port: 18084, enabled: true, type: 'newtab' },
|
{ id: 'libretranslate', name: '智能翻译', icon: '', logoImg: libretranslateLogo, description: '中英互译,支持文本和文件翻译,数据不出内网', path: '/translate/', enabled: true, type: 'newtab' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '🖼️ 图片处理',
|
name: '🖼️ 图片处理',
|
||||||
tools: [
|
tools: [
|
||||||
{ id: 'imgcompress', name: '图片压缩转换', icon: '🗜️', description: '图片压缩、格式转换、批量处理、AI 智能抠图去背景', port: 18087, enabled: true, type: 'newtab' },
|
{ id: 'imgcompress', name: '图片压缩转换', icon: '🗜️', description: '图片压缩、格式转换、批量处理、AI 智能抠图去背景', path: '/imgcompress/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'lama-cleaner', name: 'AI 图像擦除', icon: '🧹', description: 'AI 智能擦除图片中的水印、路人、瑕疵,自动修复画面', port: 18088, enabled: true, type: 'newtab' },
|
{ id: 'lama-cleaner', name: 'AI 图像擦除', icon: '🧹', description: 'AI 智能擦除图片中的水印、路人、瑕疵,自动修复画面', path: '/lama/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'webp2jpg', name: '图片格式转换', icon: '🔄', description: '支持 PSD/HEIC/WebP/PNG/JPG 等格式批量互转,纯浏览器处理不上传', port: 18089, enabled: true, type: 'newtab' },
|
{ id: 'webp2jpg', name: '图片格式转换', icon: '🔄', description: '支持 PSD/HEIC/WebP/PNG/JPG 等格式批量互转,纯浏览器处理不上传', path: '/webp2jpg/', enabled: true, type: 'newtab' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '🎨 创作绘图',
|
name: '🎨 创作绘图',
|
||||||
tools: [
|
tools: [
|
||||||
{ id: 'excalidraw', name: 'Excalidraw', icon: '', logoImg: excalidrawLogo, description: '手绘风格白板,绘制流程图、架构图、示意图', port: 18081, enabled: true, type: 'newtab' },
|
{ id: 'excalidraw', name: 'Excalidraw', icon: '', logoImg: excalidrawLogo, description: '手绘风格白板,绘制流程图、架构图、示意图', path: '/draw/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'pptist', name: 'AI PPT 编辑器', icon: '', logoImg: pptistLogo, description: 'AI 生成 PPT 大纲和演示文稿,在线编辑演示', port: 18085, enabled: true, type: 'newtab' },
|
{ id: 'pptist', name: 'AI PPT 编辑器', icon: '', logoImg: pptistLogo, description: 'AI 生成 PPT 大纲和演示文稿,在线编辑演示', path: '/ppt/', enabled: true, type: 'newtab' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '📝 科研写作',
|
name: '📝 科研写作',
|
||||||
tools: [
|
tools: [
|
||||||
{ id: 'overleaf', name: 'LaTeX 论文编辑器', icon: '📐', description: '在线 LaTeX 编辑器,支持多人协作撰写论文,实时编译预览', port: 18090, enabled: true, type: 'newtab' },
|
{ id: 'overleaf', name: 'LaTeX 论文编辑器', icon: '📐', description: '在线 LaTeX 编辑器,支持多人协作撰写论文,实时编译预览', path: '/overleaf/', enabled: true, type: 'newtab' },
|
||||||
{ id: 'latex-formula', name: 'LaTeX 公式编辑器', icon: '∑', description: '在线编辑数学公式,支持希腊字母、矩阵、积分等,可复制公式或图片', port: 18091, enabled: true, type: 'newtab' },
|
{ id: 'latex-formula', name: 'LaTeX 公式编辑器', icon: '∑', description: '在线编辑数学公式,支持希腊字母、矩阵、积分等,可复制公式或图片', path: '/latex/', enabled: true, type: 'newtab' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const TOOL_PORT = 18000;
|
||||||
|
|
||||||
const openTool = (tool: Tool) => {
|
const openTool = (tool: Tool) => {
|
||||||
if (!tool.enabled) return;
|
if (!tool.enabled) return;
|
||||||
const url = tool.url || (tool.port ? `${window.location.protocol}//${window.location.hostname}:${tool.port}${tool.path || ''}` : '');
|
let url = '';
|
||||||
|
if (tool.path) {
|
||||||
|
url = `${window.location.protocol}//${window.location.hostname}:${TOOL_PORT}${tool.path}`;
|
||||||
|
} else if (tool.port) {
|
||||||
|
url = `${window.location.protocol}//${window.location.hostname}:${tool.port}`;
|
||||||
|
}
|
||||||
if (url) {
|
if (url) {
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<div class="symbols" id="symbolBar"></div>
|
<div class="symbols" id="symbolBar"></div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn-primary" onclick="copyLatex()">复制公式</button>
|
<button class="btn-primary" onclick="copyLatex()">复制公式</button>
|
||||||
<button class="btn-secondary" onclick="copyImg()">复制为图片</button>
|
<button class="btn-secondary" onclick="downloadImg()">下载为图片</button>
|
||||||
<button class="btn-secondary" onclick="document.getElementById('input').value='';render()">清空</button>
|
<button class="btn-secondary" onclick="document.getElementById('input').value='';render()">清空</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="templates">
|
<div class="templates">
|
||||||
@@ -121,23 +121,26 @@ function copyLatex() {
|
|||||||
navigator.clipboard.writeText(document.getElementById('input').value);
|
navigator.clipboard.writeText(document.getElementById('input').value);
|
||||||
showToast('公式已复制!');
|
showToast('公式已复制!');
|
||||||
}
|
}
|
||||||
function copyImg() {
|
function downloadImg() {
|
||||||
const el = document.getElementById('preview');
|
const el = document.getElementById('preview');
|
||||||
const svg = el.querySelector('svg') || el.querySelector('.katex');
|
if (!el.querySelector('.katex')) { showToast('请先输入公式'); return; }
|
||||||
if (!svg) return;
|
// 直接用 KaTeX 的 mathml/svg 渲染结果导出为 SVG 文件下载
|
||||||
const canvas = document.createElement('canvas');
|
const w = el.scrollWidth + 40, h = el.scrollHeight + 40;
|
||||||
const ctx = canvas.getContext('2d');
|
const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}">
|
||||||
const data = new XMLSerializer().serializeToString(el);
|
<rect width="100%" height="100%" fill="white"/>
|
||||||
const img = new Image();
|
<foreignObject width="100%" height="100%">
|
||||||
const blob = new Blob(['<svg xmlns="http://www.w3.org/2000/svg" width="'+el.scrollWidth+'" height="'+el.scrollHeight+'"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="background:#fff;padding:20px">'+el.innerHTML+'</div></foreignObject></svg>'], {type:'image/svg+xml'});
|
<div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:center;justify-content:center;width:${w}px;height:${h}px;font-size:24px">
|
||||||
const url = URL.createObjectURL(blob);
|
${el.innerHTML}
|
||||||
img.onload = () => {
|
</div>
|
||||||
canvas.width = img.width * 2; canvas.height = img.height * 2;
|
</foreignObject>
|
||||||
ctx.scale(2,2); ctx.drawImage(img, 0, 0);
|
</svg>`;
|
||||||
canvas.toBlob(b => { navigator.clipboard.write([new ClipboardItem({'image/png': b})]); showToast('图片已复制!'); });
|
const blob = new Blob([svgStr], {type: 'image/svg+xml'});
|
||||||
URL.revokeObjectURL(url);
|
const a = document.createElement('a');
|
||||||
};
|
a.href = URL.createObjectURL(blob);
|
||||||
img.src = url;
|
a.download = 'formula.svg';
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(a.href);
|
||||||
|
showToast('图片已下载!');
|
||||||
}
|
}
|
||||||
function showToast(msg) {
|
function showToast(msg) {
|
||||||
const t = document.getElementById('toast');
|
const t = document.getElementById('toast');
|
||||||
|
|||||||
133
scripts/tools-nginx.conf
Normal file
133
scripts/tools-nginx.conf
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
server {
|
||||||
|
listen 18000;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
|
||||||
|
# 1. Stirling PDF
|
||||||
|
location /pdf/ {
|
||||||
|
proxy_pass http://127.0.0.1:18080/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header Accept-Encoding "";
|
||||||
|
proxy_buffering off;
|
||||||
|
sub_filter_once off;
|
||||||
|
sub_filter '"/api/' '"/pdf-api/';
|
||||||
|
}
|
||||||
|
location /pdf-api/ {
|
||||||
|
proxy_pass http://127.0.0.1:18080/api/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Excalidraw
|
||||||
|
location /draw/ {
|
||||||
|
proxy_pass http://127.0.0.1:18081/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
location /assets/ { proxy_pass http://127.0.0.1:18081/assets/; }
|
||||||
|
location /v1/ai/ {
|
||||||
|
proxy_pass http://127.0.0.1:18082/v1/ai/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. TrWebOCR
|
||||||
|
location /ocr/ {
|
||||||
|
proxy_pass http://127.0.0.1:18083/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. LibreTranslate
|
||||||
|
location /translate/ {
|
||||||
|
proxy_pass http://127.0.0.1:18084/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. PPTist
|
||||||
|
location /ppt/ {
|
||||||
|
proxy_pass http://127.0.0.1:18085/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
location /pptapi/ {
|
||||||
|
proxy_pass http://127.0.0.1:18086/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 6. imgcompress
|
||||||
|
location /imgcompress/ {
|
||||||
|
proxy_pass http://127.0.0.1:18087/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 7. Lama Cleaner
|
||||||
|
location /lama/ {
|
||||||
|
proxy_pass http://127.0.0.1:18088/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
location /lama/inpaint {
|
||||||
|
proxy_pass http://127.0.0.1:18088/inpaint;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
}
|
||||||
|
# Lama Cleaner 的2个精确static文件
|
||||||
|
location /static/js/main.1bd455bc.js { proxy_pass http://127.0.0.1:18088/static/js/main.1bd455bc.js; }
|
||||||
|
location /static/css/main.c28d98ca.css { proxy_pass http://127.0.0.1:18088/static/css/main.c28d98ca.css; }
|
||||||
|
|
||||||
|
# /static/ 其余的给 LibreTranslate
|
||||||
|
location /static/ { proxy_pass http://127.0.0.1:18084/static/; }
|
||||||
|
|
||||||
|
# LibreTranslate 绝对路径 API
|
||||||
|
location /languages { proxy_pass http://127.0.0.1:18084/languages; }
|
||||||
|
location /frontend/settings { proxy_pass http://127.0.0.1:18084/frontend/settings; }
|
||||||
|
location /detect { proxy_pass http://127.0.0.1:18084/detect; }
|
||||||
|
|
||||||
|
# imgcompress 绝对路径
|
||||||
|
location /_next/ { proxy_pass http://127.0.0.1:18087/_next/; }
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:18087/api/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_buffering off;
|
||||||
|
client_max_body_size 500M;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 8. webp2jpg
|
||||||
|
location /webp2jpg/ {
|
||||||
|
proxy_pass http://127.0.0.1:18089/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
location /cdn/ { proxy_pass http://127.0.0.1:18089/cdn/; }
|
||||||
|
|
||||||
|
# 9. Overleaf
|
||||||
|
location /overleaf/ {
|
||||||
|
proxy_pass http://127.0.0.1:18090/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 10. LaTeX 公式编辑器
|
||||||
|
location /latex/ {
|
||||||
|
proxy_pass http://127.0.0.1:18091/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 默认回落到 Overleaf
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:18090;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user