85 lines
3.6 KiB
JavaScript
85 lines
3.6 KiB
JavaScript
import {fileURLToPath, URL} from 'node:url'
|
|
import {execSync} from 'node:child_process'
|
|
import {existsSync} from 'node:fs'
|
|
|
|
import {defineConfig} from 'vite'
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
|
// Vite's dev-server SPA fallback only rewrites a request to index.html when the last path
|
|
// segment has no dot (it sniffs for a file extension). A route whose only/last segment is a
|
|
// URL-encoded handle (e.g. /groups/+user@a.localhost, see encodeHandleForUrl in src/router.js)
|
|
// legitimately contains a dot from the domain part, so a direct load/refresh 404s there unless
|
|
// we pre-empt that check ourselves. Gating on both a text/html Accept header and an '@' in the
|
|
// last segment keeps this from ever matching real asset requests or vite's own internal
|
|
// @fs/@id/@vite module paths (which never carry an html Accept header).
|
|
function handleUrlFallback() {
|
|
return {
|
|
name: 'handle-url-fallback',
|
|
configureServer(server) {
|
|
server.middlewares.use((req, res, next) => {
|
|
const accept = req.headers.accept || ''
|
|
const pathname = (req.url || '').split('?')[0]
|
|
const lastSegment = pathname.slice(pathname.lastIndexOf('/') + 1)
|
|
if (req.method === 'GET' && accept.includes('text/html') && lastSegment.includes('@')) {
|
|
req.url = '/index.html'
|
|
}
|
|
next()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
function gitCommit() {
|
|
// deploy/dev/docker-compose.yml bind-mounts the repo's real .git dir at
|
|
// /git (separate from /app, which only has the frontend/ subtree) -
|
|
// point git at it explicitly there. Bare-metal dev has no such mount,
|
|
// but this file's cwd sits inside the real checkout so plain rev-parse
|
|
// finds it by walking up. In prod, the build context is frontend/ alone
|
|
// with no .git anywhere, so both fail and we fall back to the
|
|
// GIT_COMMIT build-arg/env var (see deploy/prod/Dockerfile.frontend and
|
|
// playbook.yml).
|
|
const gitDirFlag = existsSync('/git') ? '--git-dir=/git ' : ''
|
|
try {
|
|
return execSync(`git ${gitDirFlag}rev-parse --short HEAD`).toString().trim()
|
|
} catch {
|
|
return process.env.GIT_COMMIT || 'unknown'
|
|
}
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins: [vue(), handleUrlFallback()],
|
|
define: {
|
|
__GIT_COMMIT__: JSON.stringify(gitCommit())
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
}
|
|
},
|
|
server: {
|
|
host: true,
|
|
cors: true,
|
|
headers: {
|
|
//allow all origins
|
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Auth-Token, Authorization, Accept, charset, boundary, Content-Length',
|
|
'Access-Control-Allow-Credentials': 'true',
|
|
'Access-Control-Max-Age': '86400',
|
|
//'Upgrade-Insecure-Requests': '1',
|
|
'Content-Security-Policy': 'default-src \'self\';'
|
|
+ ' script-src \'self\' \'wasm-unsafe-eval\' \'unsafe-eval\' \'unsafe-inline\';'
|
|
// Without this, worker-src falls back to script-src, which has no blob: source -
|
|
// CameraScanner.vue's decode Worker (built from a Blob URL) would be silently
|
|
// blocked by CSP rather than the app's own capability checks.
|
|
+ ' worker-src \'self\' blob:;'
|
|
+ ' style-src \'self\' \'unsafe-inline\';'
|
|
+ ' img-src \'self\' * data: blob:;'
|
|
+ ' connect-src * data:',
|
|
},
|
|
},
|
|
test: {
|
|
include: ['src/tests/**/*.js'],
|
|
globals: true,
|
|
environment: "jsdom"
|
|
}
|
|
})
|