diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..6718841
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,18 @@
+PORT_FRONTEND=5173
+PORT_FRONTEND_HMR=3002
+PORT_API=800
+PORT_SUPABASE=8000
+
+HOST_FRONTEND=localhost:5173
+VITE_PORT_FRONTEND=5173
+VITE_PORT_FRONTEND_HMR=5173
+
+VITE_APP_NAME=Classroom Copilot
+VITE_SUPER_ADMIN_EMAIL=admin@classroomcopilot.ai
+VITE_DEV=true
+VITE_FRONTEND_SITE_URL=http://localhost:5173
+VITE_APP_HMR_URL=http://localhost:5173
+VITE_SUPABASE_URL=http://localhost:8000
+VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiaWF0IjoxNzcxNzAwMTAwLCJpc3MiOiJzdXBhYmFzZSIsInN1YiI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsImV4cCI6MjA4NzA2MDEwMCwicm9sZSI6ImFub24ifQ.-ZIBd7I6DeBgIlj_JJMvrvPqvdrQAMDuOvp-zddDsmc
+VITE_API_URL=http://localhost:8080
+VITE_API_BASE=http://localhost:8080
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 6368c1f..833a1ed 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,177 +1,122 @@
-///
-///
+import { defineConfig, loadEnv } from 'vite'
+import react from '@vitejs/plugin-react'
+import { VitePWA } from 'vite-plugin-pwa'
+import path from 'path'
-import { defineConfig, loadEnv, UserConfig, ConfigEnv } from 'vite';
-import react from '@vitejs/plugin-react';
-import process from 'node:process';
-import { VitePWA } from 'vite-plugin-pwa';
+// https://vitejs.dev/config/
+export default defineConfig(({ mode }) => {
+ // Load env file based on mode (development, production, etc.)
+ // This loads .env.development for dev mode
+ const env = loadEnv(mode, process.cwd(), '')
-export default defineConfig(async ({ mode }: ConfigEnv): Promise => {
- // Load env file based on mode in correct order
- const env = loadEnv(mode, process.cwd(), 'VITE_');
+ // Check if we're behind an SSL proxy
+ const isSSLProxy = env.VITE_FRONTEND_SITE_URL?.startsWith('https://')
- // Determine base URL from hostname
- const base = '/'; // Always use root path, let nginx handle the routing
-
- // Determine if we're in production based on mode and VITE_DEV flag
- const isProd = mode === 'production' && env.VITE_DEV !== 'true';
+ // Determine client-side env vars to expose
+ const envPrefix = 'VITE_'
+ const clientEnv = Object.fromEntries(
+ Object.entries(env)
+ .filter(([key]) => key.startsWith(envPrefix))
+ .map(([key, value]) => [`import.meta.env.${key}`, JSON.stringify(value)])
+ )
return {
plugins: [
react(),
VitePWA({
+ registerType: 'autoUpdate',
+ injectRegister: 'inline',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
- registerType: 'prompt',
- injectRegister: 'auto',
devOptions: {
- enabled: false,
- type: 'module'
+ enabled: true,
+ type: 'module',
},
manifest: {
- name: 'ClassroomCopilot',
- short_name: 'CC',
- start_url: base,
- scope: base,
- display: 'fullscreen',
+ name: 'Classroom Copilot',
+ short_name: 'Classroom Copilot',
+ description: 'AI-powered teaching assistant',
+ theme_color: '#ffffff',
background_color: '#ffffff',
- theme_color: '#000000',
+ display: 'standalone',
+ scope: '/',
+ start_url: '/',
icons: [
{
src: '/icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png',
- purpose: 'any'
},
{
src: '/icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png',
- purpose: 'any'
},
{
src: '/icons/icon-192x192-maskable.png',
sizes: '192x192',
type: 'image/png',
- purpose: 'maskable'
+ purpose: 'maskable',
},
{
src: '/icons/icon-512x512-maskable.png',
sizes: '512x512',
type: 'image/png',
- purpose: 'maskable'
- }
- ]
+ purpose: 'maskable',
+ },
+ ],
},
- includeAssets: ['favicon.ico', 'icons/*.png'],
- injectManifest: {
- globPatterns: [
- 'index.html',
- '**/*.{js,css,html,ico,png,svg,json}',
- 'manifest.webmanifest'
- ],
- maximumFileSizeToCacheInBytes: 8 * 1024 * 1024, // 8MB
- dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
- // Exclude development resources and source files
- globIgnores: [
- '**/node_modules/**/*',
- 'sw.js',
- 'workbox-*.js',
- '**/*.map',
- '**/vite/**/*',
- '**/@vite/**/*',
- '**/@react-refresh/**/*'
- ],
- // Ensure service worker has correct scope
- swDest: 'dist/sw.js',
- manifestTransforms: [
- // Transform manifest entries to ensure proper caching
- (entries: any[]) => ({
- manifest: entries.map(entry => ({
- ...entry,
- url: entry.url.startsWith(base) ? entry.url : `${base}${entry.url.startsWith('/') ? entry.url.slice(1) : entry.url}`
- }))
- })
- ]
- }
- })
+ workbox: {
+ globPatterns: ['**/*.{js,css,html,ico,png,svg,json,vue,txt,woff2}'],
+ cleanupOutdatedCaches: true,
+ clientsClaim: true,
+ skipWaiting: true,
+ },
+ }),
],
+
+ // Define client-side env vars
define: {
- // Make env variables available globally
- 'process.env': env,
- // Ensure import.meta.env has our variables
- 'import.meta.env.VITE_FRONTEND_SITE_URL': JSON.stringify(env.VITE_FRONTEND_SITE_URL),
- 'import.meta.env.VITE_SUPABASE_URL': JSON.stringify(env.VITE_SUPABASE_URL),
- 'import.meta.env.VITE_SUPABASE_ANON_KEY': JSON.stringify(env.VITE_SUPABASE_ANON_KEY),
- 'import.meta.env.VITE_SUPER_ADMIN_EMAIL': JSON.stringify(env.VITE_SUPER_ADMIN_EMAIL),
- 'import.meta.env.VITE_DEV': env.VITE_DEV === 'true',
- 'import.meta.env.VITE_API_BASE': JSON.stringify(env.VITE_API_BASE),
- 'import.meta.env.VITE_SEARCH_URL': JSON.stringify(env.VITE_SEARCH_URL),
+ ...clientEnv,
+ // Explicitly define VITE_DEV for runtime checks
+ 'import.meta.env.VITE_DEV': JSON.stringify(env.VITE_DEV || 'false'),
+ 'import.meta.env.DEV': mode === 'development',
+ 'import.meta.env.PROD': mode === 'production',
},
- envPrefix: 'VITE_',
- base,
+
server: {
host: '0.0.0.0',
port: parseInt(env.VITE_PORT_FRONTEND || '5173'),
- watch: {
- usePolling: env.VITE_DEV === 'true',
- ignored: ['**/node_modules/**', '**/dist/**']
+ strictPort: true,
+
+ // HMR configuration for SSL proxy
+ hmr: isSSLProxy ? {
+ // When behind SSL proxy, use WSS and public host
+ protocol: 'wss',
+ host: '192.168.0.94',
+ port: 5173,
+ clientPort: 5173,
+ } : {
+ // Direct HTTP development
+ protocol: 'ws',
+ host: 'localhost',
+ port: parseInt(env.VITE_PORT_FRONTEND_HMR || '5173'),
},
- allowedHosts: [
- `app.${env.VITE_FRONTEND_SITE_URL}`,
- ],
- hmr: isProd ? false : {
- protocol: env.VITE_DEV === 'true' ? 'ws' : 'wss',
- host: env.VITE_DEV == 'true' ? 'localhost' : env.VITE_APP_HMR_URL,
- port: parseInt(env.VITE_PORT_FRONTEND || '5173'),
- clientPort: parseInt(env.VITE_PORT_FRONTEND_HMR || '5173'),
- overlay: false
- },
- proxy: {
- '/searxng-api': {
- target: env.VITE_SEARCH_URL,
- changeOrigin: true,
- rewrite: (path: string) => path.replace(/^\/searxng-api/, '')
- }
- }
- },
- clearScreen: false,
- optimizeDeps: {
- force: true,
- include: ['react', 'react-dom', '@mui/material', '@tldraw/tldraw']
+
+ // Allow all origins for development
+ cors: true,
},
+
build: {
- sourcemap: !isProd,
- manifest: true,
- minify: isProd ? 'terser' : false,
- terserOptions: isProd ? {
- compress: {
- drop_debugger: true
- }
- } : undefined,
- rollupOptions: {
- output: {
- // Ensure chunk filenames include content hash
- chunkFileNames: isProd ? 'assets/[name].[hash].js' : 'assets/[name].js',
- assetFileNames: isProd ? 'assets/[name].[hash][extname]' : 'assets/[name][extname]'
- },
- // Externalize dependencies that shouldn't be bundled
- external: isProd ? [] : [/^@vite/, /^@react-refresh/]
- },
- chunkSizeWarningLimit: 2000,
- // Enable module concatenation for better minification
- target: 'esnext',
- cssCodeSplit: true,
- assetsInlineLimit: 4096, // 4kb
- modulePreload: true,
- reportCompressedSize: !isProd
+ outDir: 'dist',
+ sourcemap: mode === 'development',
},
- // Add esbuild optimization
- esbuild: {
- drop: isProd ? ['debugger'] : [],
- legalComments: 'none',
- target: ['esnext']
- }
- };
-});
+
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ }
+})