2026-09-03 21:01:52 +03:30
|
|
|
import { defineConfig, loadEnv } from 'vite';
|
2025-11-18 20:22:25 +03:30
|
|
|
import react from '@vitejs/plugin-react';
|
|
|
|
|
import { VitePWA } from 'vite-plugin-pwa';
|
|
|
|
|
import path from 'path';
|
|
|
|
|
|
|
|
|
|
// https://vitejs.dev/config/
|
2026-09-03 21:01:52 +03:30
|
|
|
export default defineConfig(({ mode }) => {
|
|
|
|
|
const env = loadEnv(mode, process.cwd(), '');
|
|
|
|
|
const aiBaseUrl = (env.VITE_OPENAI_BASE_URL || 'https://openrouter.ai/api/v1').replace(/\/$/, '');
|
|
|
|
|
|
|
|
|
|
let proxyTarget = 'https://openrouter.ai';
|
|
|
|
|
let proxyPathPrefix = '/api/v1';
|
|
|
|
|
try {
|
|
|
|
|
const parsed = new URL(aiBaseUrl);
|
|
|
|
|
proxyTarget = parsed.origin;
|
|
|
|
|
proxyPathPrefix = parsed.pathname.replace(/\/$/, '') || '';
|
|
|
|
|
} catch {
|
|
|
|
|
// keep defaults
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
plugins: [
|
|
|
|
|
react(),
|
|
|
|
|
VitePWA({
|
|
|
|
|
registerType: 'autoUpdate',
|
2026-09-09 01:31:24 +03:30
|
|
|
includeAssets: [
|
|
|
|
|
'favicon.ico',
|
|
|
|
|
'favicon-16x16.png',
|
|
|
|
|
'favicon-32x32.png',
|
|
|
|
|
'apple-touch-icon.png',
|
|
|
|
|
'logo/mainlogo.png',
|
|
|
|
|
],
|
2026-09-03 21:01:52 +03:30
|
|
|
manifest: {
|
|
|
|
|
name: 'نویا',
|
|
|
|
|
short_name: 'نویا',
|
|
|
|
|
description: 'پلتفرم روانشناسی و سلامت روان نویا',
|
|
|
|
|
theme_color: '#0a0f1d',
|
|
|
|
|
background_color: '#070b14',
|
|
|
|
|
display: 'standalone',
|
|
|
|
|
orientation: 'portrait',
|
|
|
|
|
scope: '/',
|
|
|
|
|
start_url: '/',
|
|
|
|
|
dir: 'rtl',
|
|
|
|
|
lang: 'fa',
|
|
|
|
|
icons: [
|
|
|
|
|
{
|
|
|
|
|
src: 'pwa-192x192.png',
|
|
|
|
|
sizes: '192x192',
|
|
|
|
|
type: 'image/png'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
src: 'pwa-512x512.png',
|
|
|
|
|
sizes: '512x512',
|
|
|
|
|
type: 'image/png'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
src: 'pwa-512x512.png',
|
|
|
|
|
sizes: '512x512',
|
|
|
|
|
type: 'image/png',
|
|
|
|
|
purpose: 'any maskable'
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-05 14:59:18 +03:30
|
|
|
},
|
2026-09-03 21:01:52 +03:30
|
|
|
workbox: {
|
|
|
|
|
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}']
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
],
|
|
|
|
|
resolve: {
|
|
|
|
|
alias: {
|
|
|
|
|
'@': path.resolve(__dirname, './src')
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
server: {
|
|
|
|
|
proxy: {
|
|
|
|
|
// Browser → Vite → local Backend (avoids CORS on localhost)
|
|
|
|
|
'/api/backend': {
|
|
|
|
|
target: 'http://localhost:3001',
|
|
|
|
|
changeOrigin: true,
|
|
|
|
|
rewrite: (requestPath) => requestPath.replace(/^\/api\/backend/, ''),
|
|
|
|
|
},
|
|
|
|
|
// Browser → Vite → OpenRouter (avoids CORS / blocked browser routes on LAN)
|
|
|
|
|
'/api/ai': {
|
|
|
|
|
target: proxyTarget,
|
|
|
|
|
changeOrigin: true,
|
|
|
|
|
secure: true,
|
|
|
|
|
timeout: 180_000,
|
|
|
|
|
proxyTimeout: 180_000,
|
|
|
|
|
rewrite: (requestPath) => requestPath.replace(/^\/api\/ai/, proxyPathPrefix),
|
|
|
|
|
configure: (proxy) => {
|
|
|
|
|
proxy.on('proxyReq', (proxyReq, req) => {
|
|
|
|
|
// Avoid forwarding browser Origin; set stable OpenRouter attribution headers
|
|
|
|
|
proxyReq.removeHeader('origin');
|
|
|
|
|
proxyReq.setHeader('HTTP-Referer', 'https://novya.app');
|
|
|
|
|
proxyReq.setHeader('X-Title', 'Novya');
|
|
|
|
|
if (!proxyReq.getHeader('user-agent')) {
|
|
|
|
|
proxyReq.setHeader(
|
|
|
|
|
'User-Agent',
|
|
|
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// NOTE: do NOT use import.meta.env here — undefined in Node proxy callbacks
|
|
|
|
|
console.log('[AI Proxy]', req.method, req.url, '→', proxyTarget + proxyPathPrefix);
|
|
|
|
|
});
|
|
|
|
|
proxy.on('error', (err, _req, res) => {
|
|
|
|
|
console.error('[AI Proxy] Error:', err.message);
|
|
|
|
|
const socket = res as {
|
|
|
|
|
headersSent?: boolean;
|
|
|
|
|
writeHead?: (code: number, headers: Record<string, string>) => void;
|
|
|
|
|
end?: (body: string) => void;
|
|
|
|
|
};
|
|
|
|
|
if (socket && !socket.headersSent && typeof socket.writeHead === 'function') {
|
|
|
|
|
socket.writeHead(502, { 'Content-Type': 'application/json' });
|
|
|
|
|
socket.end?.(
|
|
|
|
|
JSON.stringify({
|
|
|
|
|
error: 'AI proxy error',
|
|
|
|
|
message: String(err.message || err)
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-01-05 14:59:18 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-09-03 21:01:52 +03:30
|
|
|
};
|
2025-11-18 20:22:25 +03:30
|
|
|
});
|