Files
pwa/vite.config.ts
ar.badieiandCursor 8016b8f45e Polish Novya UI with brand colors, first-load skeletons, and session transition overlay.
Keep chat pinned to the latest message while AI thinks or streams, and align buttons/inputs with the logo teal/blue focus treatment.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 01:31:24 +03:30

128 lines
4.2 KiB
TypeScript

import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
import path from 'path';
// https://vitejs.dev/config/
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',
includeAssets: [
'favicon.ico',
'favicon-16x16.png',
'favicon-32x32.png',
'apple-touch-icon.png',
'logo/mainlogo.png',
],
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'
}
]
},
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)
})
);
}
});
}
}
}
}
};
});