首次提交
Azure Static Web Apps CI/CD / Close Pull Request Job (push) Skipped
Build and Deploy / build (push) Failing after 9m48s
Azure Static Web Apps CI/CD / Build and Deploy Job (push) Failing after 9m50s

This commit is contained in:
2026-10-24 01:29:24 +08:00
parent e70d46436c
commit c7fdc11dbd
146 changed files with 9750 additions and 5578 deletions
+125 -3
View File
@@ -13,11 +13,132 @@ import { VitePWA } from 'vite-plugin-pwa'
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
import vueDevTools from 'vite-plugin-vue-devtools'
import fs from 'node:fs'
import path from 'node:path'
// Local File Storage Plugin
const localFileStoragePlugin = () => {
return {
name: 'local-file-storage',
configureServer(server) {
const dataDir = path.resolve(process.cwd(), 'data');
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
server.middlewares.use('/api/ppt', async (req, res, next) => {
const url = new URL(req.url, `http://${req.headers.host}`);
// Handle CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.end();
return;
}
if (url.pathname === '/check' || url.pathname.endsWith('/check')) {
res.setHeader('Content-Type', 'application/json');
// In dev mode on node, we just mock it. Real logic is in start-server.ps1
res.end(JSON.stringify({ success: true, hasPpt: false }));
return;
}
if (url.pathname === '/control' || url.pathname.endsWith('/control')) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
return;
}
next();
});
server.middlewares.use('/api/local-file', async (req, res, next) => {
const url = new URL(req.url, `http://${req.headers.host}`);
const key = url.searchParams.get('key');
// Handle CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.end();
return;
}
if (url.pathname === '/save' && req.method === 'POST') {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
try {
const { key, data } = JSON.parse(body);
if (!key || !data) throw new Error('Missing key or data');
// Sanitize key to prevent path traversal
const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
const filePath = path.join(dataDir, `${safeKey}.json`);
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
} catch (e) {
res.statusCode = 500;
res.end(JSON.stringify({ success: false, error: e.message }));
}
});
return;
}
if (url.pathname === '/load' && req.method === 'GET') {
if (!key) {
res.statusCode = 400;
res.end(JSON.stringify({ success: false, error: 'Missing key' }));
return;
}
const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
const filePath = path.join(dataDir, `${safeKey}.json`);
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf-8');
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true, data: JSON.parse(data) }));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ success: false, error: 'Not found' }));
}
return;
}
if (url.pathname === '/keys' && req.method === 'GET') {
try {
const files = fs.readdirSync(dataDir);
const keys = files
.filter(f => f.endsWith('.json'))
.map(f => f.replace('.json', ''));
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true, keys }));
} catch (e) {
res.statusCode = 500;
res.end(JSON.stringify({ success: false, error: e.message }));
}
return;
}
next();
});
}
}
}
// https://vitejs.dev/config/
export default defineConfig({
base: './',
plugins: [
localFileStoragePlugin(),
VueRouter(),
vueDevTools(),
Layouts(),
@@ -168,7 +289,7 @@ export default defineConfig({
google: {
families: [{
name: 'Roboto',
styles: 'wght@100;300;400;500;700;900',
styles: 'wght@300;400;500;700',
}],
},
}),
@@ -208,8 +329,6 @@ export default defineConfig({
'vendor-vue': ['vue', 'vue-router', 'pinia'],
// UI 框架
'vendor-vuetify': ['vuetify'],
// 监控(异步加载,独立 chunk)
'vendor-sentry': ['@sentry/vue'],
// 实时通信
'vendor-socket': ['socket.io-client'],
// 通用工具库
@@ -220,6 +339,9 @@ export default defineConfig({
},
server: {
port: 3031,
watch: {
ignored: ['**/data/**', path.resolve(process.cwd(), 'data', '**')]
}
},
css: {
preprocessorOptions: {