chore: initial commit - CloudSearch v0.0.2

This commit is contained in:
2026-05-15 05:50:50 +08:00
commit d83225d736
102 changed files with 37926 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
import { Router, Request, Response } from 'express';
import multer from 'multer';
import sharp from 'sharp';
import path from 'path';
import fs from 'fs';
import { authMiddleware } from '../admin/auth.service';
import { updateSystemConfig } from '../admin/system-config.service';
const router = Router();
// ============ Upload ============
/**
* POST /api/admin/upload-fallback-image
* Upload a fallback cover image for search results without covers.
* Recommended: 320×180 JPEG/PNG (16:9), max 2MB.
*/
const uploadDir = path.resolve('/app/uploads/fallback');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
const fallbackStorage = multer.diskStorage({
destination: (_req, _file, cb) => cb(null, uploadDir),
filename: (_req, _file, cb) => {
const ext = '.jpg';
cb(null, `fallback_cover_tmp${ext}`);
},
});
const upload = multer({
storage: fallbackStorage,
limits: { fileSize: 2 * 1024 * 1024 }, // 2MB max
fileFilter: (_req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('仅支持图片文件JPEG/PNG'));
}
},
});
router.post('/admin/upload-fallback-image', authMiddleware, upload.single('image'), async (req: Request, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ error: '请选择要上传的图片' });
return;
}
// 压缩最大宽度320pxJPEG quality 80
const outPath = path.resolve(uploadDir, 'fallback_cover.jpg');
await sharp(req.file.path)
.resize(320, undefined, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 80 })
.toFile(outPath);
// 删除原始上传文件(如果路径不同)
if (req.file.path !== outPath) {
fs.unlink(req.file.path, () => {});
}
const url = `/api/uploads/fallback/fallback_cover.jpg`;
updateSystemConfig('search_fallback_image', url);
const stat = fs.statSync(outPath);
res.json({ success: true, url, message: `✅ 兜底图已压缩上传 (${(stat.size / 1024).toFixed(1)}KB)` });
} catch (err: any) {
res.status(500).json({ error: err.message || '上传失败' });
}
});
/**
* POST /api/admin/upload-logo
* Upload a site logo image displayed on search page (home link) and homepage.
* Recommended: 320×60 or similar wide/banner ratio, JPEG/PNG/WebP, max 2MB.
*/
const logoUploadDir = path.resolve('/app/uploads/logo');
if (!fs.existsSync(logoUploadDir)) {
fs.mkdirSync(logoUploadDir, { recursive: true });
}
const logoStorage = multer.diskStorage({
destination: (_req, _file, cb) => cb(null, logoUploadDir),
filename: (_req, _file, cb) => {
cb(null, `site_logo_tmp.png`);
},
});
const logoUpload = multer({
storage: logoStorage,
limits: { fileSize: 2 * 1024 * 1024 }, // 2MB max
fileFilter: (_req, file, cb) => {
if (file.mimetype.startsWith('image/')) {
cb(null, true);
} else {
cb(new Error('仅支持图片文件JPEG/PNG/WebP'));
}
},
});
router.post('/admin/upload-logo', authMiddleware, logoUpload.single('image'), async (req: Request, res: Response) => {
try {
if (!req.file) {
res.status(400).json({ error: '请选择要上传的图片' });
return;
}
// 压缩最大宽度640pxPNG格式
const outPath = path.resolve(logoUploadDir, 'site_logo.png');
await sharp(req.file.path)
.resize(640, undefined, { fit: 'inside', withoutEnlargement: true })
.png({ compressionLevel: 9 })
.toFile(outPath);
if (req.file.path !== outPath) {
fs.unlink(req.file.path, () => {});
}
const url = `/api/uploads/logo/site_logo.png`;
updateSystemConfig('site_logo', url);
const stat = fs.statSync(outPath);
res.json({ success: true, url, message: `✅ 站点图标已压缩上传 (${(stat.size / 1024).toFixed(1)}KB)` });
} catch (err: any) {
res.status(500).json({ error: err.message || '上传失败' });
}
});
import { startQrLogin, getQrLoginStatus, cancelQrLogin } from '../cloud/qr-login.service';
// ===== 夸克扫码登录 (不需要 auth用户未登录时也需要能用) =====
export default router;