Ctrl K
ComunidadGuías…

web.vite.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vuetify from 'vite-plugin-vuetify';
import { config as loadDotenv } from 'dotenv';
import { vuetifyComponentModules } from './vite.vuetify-deps';

// Mirrors src/main/index.ts's readConnectDefaults() — same precedence
// (explicit CONNECT_TOKEN in connect/.env, then the backend's own persisted
// connect/.connect-token, same PORT default) so this config's dev-server
// proxy (see below) can stand in for nginx's identical job in the real
// Docker deployment (see ng.conf.template's X-Connect-Token injection) —
// nothing the renderer itself needs to know about either way
// (window.__CONNECT_TOKEN__ is deliberately never set, see
// settings.js.template's comment; loadConnectDefaults() hardcodes '' in the
// web build for the same reason).
function readConnectDevDefaults(): { token: string; port: string } {
    const connectDir = resolve('connect');
    const envPath = resolve(connectDir, '.env');
    const parsed = existsSync(envPath) ? (loadDotenv({ path: envPath }).parsed ?? {}) : {};
    const port = parsed.PORT || '7071';

    let token = parsed.CONNECT_TOKEN ?? '';
    if (!token) {
        const tokenFile = resolve(connectDir, '.connect-token');
        if (existsSync(tokenFile)) token = readFileSync(tokenFile, 'utf-8').trim();
    }
    return { token, port };
}

// Plain web build of the renderer only (no main/preload) — used by the
// Docker image (see Dockerfile's frontend-builder stage), served by nginx.
// Same renderer setup as electron.vite.config.ts's `renderer` block (alias,
// plugins, root), just without electron-vite's multi-target orchestration
// and with its own output directory so it can't collide with `npm run
// build`'s out/renderer.
export default defineConfig(() => {
    const { token, port } = readConnectDevDefaults();

    return {
        resolve: {
            alias: {
                '@': resolve('src/renderer/src'),
            },
        },
        plugins: [vue(), vuetify({ autoImport: true })],
        root: 'src/renderer',
        optimizeDeps: {
            include: vuetifyComponentModules,
        },
        build: {
            outDir: resolve('out/web'),
            emptyOutDir: true,
        },
        server: {
            // Not just localhost — `pnpm dev:web` exists specifically to test
            // the mobile view (see composables/useIsMobileWeb.ts) from an
            // actual phone on the same LAN, same as the real deployment's
            // `network_mode: host`. Vite prints the resulting "Network:"
            // URL to open on the phone.
            host: true,
            port: 5273,
            // Mirrors all four of ng.conf.template's proxied location blocks,
            // not just /api — connectUrl (used by SubsonicClient for /rest/*,
            // and for /auth/*, /stream) defaults to '' in the web build (see
            // loadConnectDefaults()), i.e. bare unprefixed paths against this
            // same origin, exactly like nginx's own /rest/, /auth/, /stream
            // blocks expect. Missing any one of these doesn't 404 loudly —
            // it silently falls through to vite's SPA index.html fallback,
            // which then fails JSON.parse() at the call site instead.
            proxy: {
                // Same prefix loadConnectDefaults() falls back to
                // (window.__CONNECT_URL__ ?? '/api') when no settings.js has
                // set it — nothing to configure on the renderer side for
                // this to already line up.
                '/api': {
                    target: `http://127.0.0.1:${port}`,
                    changeOrigin: true,
                    // Mirrors ng.conf.template's trailing-slash proxy_pass
                    // trick, which strips the "/api" prefix down to the
                    // backend root (see connect/routes/proxy.py's own
                    // "nginx strips /api/" comment — same assumption here).
                    rewrite: (path) => path.replace(/^\/api/, ''),
                    configure(proxy) {
                        proxy.on('proxyReq', (proxyReq) => {
                            proxyReq.setHeader('X-Connect-Token', token);
                        });
                    },
                },
                // /rest, /auth, /stream all pass straight through unprefixed
                // (connect/routes/proxy.py's own /rest/{path} route matches
                // this literally, same as a direct Electron connection would
                // hit) — no rewrite needed, just the token injection.
                '/rest': {
                    target: `http://127.0.0.1:${port}`,
                    changeOrigin: true,
                    configure(proxy) {
                        proxy.on('proxyReq', (proxyReq) => {
                            proxyReq.setHeader('X-Connect-Token', token);
                        });
                    },
                },
                '/auth': {
                    target: `http://127.0.0.1:${port}`,
                    changeOrigin: true,
                    configure(proxy) {
                        proxy.on('proxyReq', (proxyReq) => {
                            proxyReq.setHeader('X-Connect-Token', token);
                        });
                    },
                },
                // Party guests, like nginx's own /party/ location: straight
                // through and deliberately without the token - guests
                // authenticate with their own cookie (routes/party.py).
                '/party': {
                    target: `http://127.0.0.1:${port}`,
                },
                '/stream': {
                    target: `http://127.0.0.1:${port}`,
                    changeOrigin: true,
                    configure(proxy) {
                        proxy.on('proxyReq', (proxyReq) => {
                            proxyReq.setHeader('X-Connect-Token', token);
                        });
                    },
                },
            },
        },
    };
});