Major refactor of frontend source tree such that paths better match where they are served.
This commit is contained in:
@@ -57,7 +57,7 @@ function showAuthIframe() {
|
||||
authIframe = document.createElement('iframe')
|
||||
authIframe.id = 'auth-iframe'
|
||||
authIframe.title = 'Authentication'
|
||||
authIframe.src = '/auth/api/restricted?mode=login'
|
||||
authIframe.src = '/auth/restricted?mode=login'
|
||||
document.body.appendChild(authIframe)
|
||||
loadingMessage.value = 'Authentication required...'
|
||||
}
|
||||
@@ -7,10 +7,10 @@ import RegistrationLinkModal from '@/components/RegistrationLinkModal.vue'
|
||||
import StatusMessage from '@/components/StatusMessage.vue'
|
||||
import LoadingView from '@/components/LoadingView.vue'
|
||||
import AuthRequiredMessage from '@/components/AccessDenied.vue'
|
||||
import AdminOverview from './AdminOverview.vue'
|
||||
import AdminOrgDetail from './AdminOrgDetail.vue'
|
||||
import AdminUserDetail from './AdminUserDetail.vue'
|
||||
import AdminDialogs from './AdminDialogs.vue'
|
||||
import AdminOverview from '@/admin/AdminOverview.vue'
|
||||
import AdminOrgDetail from '@/admin/AdminOrgDetail.vue'
|
||||
import AdminUserDetail from '@/admin/AdminUserDetail.vue'
|
||||
import AdminDialogs from '@/admin/AdminDialogs.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { getSettings, adminUiPath, makeUiHref } from '@/utils/settings'
|
||||
|
||||
@@ -330,7 +330,7 @@ function showAuthIframe() {
|
||||
authIframe = document.createElement('iframe')
|
||||
authIframe.id = 'auth-iframe'
|
||||
authIframe.title = 'Authentication'
|
||||
authIframe.src = '/auth/api/restricted?mode=login'
|
||||
authIframe.src = '/auth/restricted?mode=login'
|
||||
document.body.appendChild(authIframe)
|
||||
loadingMessage.value = 'Authentication required...'
|
||||
}
|
||||
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="admin-app"></div>
|
||||
<script type="module" src="/src/admin/main.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../assets/style.css'
|
||||
import '@/assets/style.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<script type="module" src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
import './assets/style.css'
|
||||
import '@/assets/style.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
@@ -0,0 +1,4 @@
|
||||
<html style="background: transparent"><meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/auth/restricted/main.js"></script>
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import RestrictedApi from './RestrictedApi.vue'
|
||||
import '@/assets/style.css'
|
||||
|
||||
createApp(RestrictedApi).mount('#app')
|
||||
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/restricted/forward/main.js"></script>
|
||||
<script type="module" src="/int/forward/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import App from './RestrictedForward.vue'
|
||||
import '@/assets/style.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/host/main.js"></script>
|
||||
<script type="module" src="/int/host/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/reset/main.js"></script>
|
||||
<script type="module" src="/int/reset/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<html style="background: transparent"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><div id="app"></div><script type="module" src="/src/restricted/api/main.js"></script>
|
||||
@@ -85,7 +85,7 @@ const headerMessage = computed(() => {
|
||||
if (props.mode === 'reauth') {
|
||||
return 'Please verify your identity to continue with this action.'
|
||||
}
|
||||
return currentView.value === 'forbidden' ? 'You lack the required permissions.' : 'Please sign in with your Passkey.'
|
||||
return currentView.value === 'forbidden' ? 'You lack the required permissions.' : 'Please sign in with your passkey.'
|
||||
})
|
||||
|
||||
const userDisplayName = computed(() => userInfo.value?.user?.user_name || 'User')
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import '@/assets/style.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
+82
-31
@@ -2,52 +2,92 @@ import { fileURLToPath, URL } from 'node:url'
|
||||
import { defineConfig } from 'vite'
|
||||
import { resolve } from 'node:path'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { readFileSync, existsSync, statSync } from 'node:fs'
|
||||
import { readFileSync, existsSync, statSync, renameSync, mkdirSync } from 'node:fs'
|
||||
|
||||
export default defineConfig(({ command }) => ({
|
||||
appType: 'mpa',
|
||||
publicDir: 'public',
|
||||
plugins: [
|
||||
vue(),
|
||||
{
|
||||
name: 'serve-examples',
|
||||
configureServer(server) {
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const url = req.url?.split('?')[0]
|
||||
if (url === '/examples') return res.writeHead(301, { Location: '/examples/' }).end()
|
||||
if (url?.startsWith('/examples/')) {
|
||||
const file = resolve(__dirname, '../examples', url === '/examples/' ? 'index.html' : url.slice(10))
|
||||
if (existsSync(file) && statSync(file).isFile()) {
|
||||
res.setHeader('Content-Type', { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript' }[file.slice(file.lastIndexOf('.'))] || 'text/plain')
|
||||
return res.end(readFileSync(file))
|
||||
}
|
||||
return res.writeHead(404).end()
|
||||
name: 'move-html-files',
|
||||
closeBundle() {
|
||||
if (command !== 'build') return
|
||||
|
||||
const outDir = resolve(__dirname, '../passkey/frontend-build')
|
||||
const moves = [
|
||||
{ from: 'auth.html', to: 'auth/index.html' },
|
||||
{ from: 'admin.html', to: 'admin/index.html' },
|
||||
{ from: 'restricted.html', to: 'restricted/index.html' },
|
||||
{ from: 'host.html', to: 'host/index.html' },
|
||||
{ from: 'reset.html', to: 'reset/index.html' },
|
||||
{ from: 'forward.html', to: 'forward/index.html' }
|
||||
]
|
||||
|
||||
for (const { from, to } of moves) {
|
||||
const fromPath = resolve(outDir, from)
|
||||
const toPath = resolve(outDir, to)
|
||||
if (existsSync(fromPath)) {
|
||||
mkdirSync(resolve(outDir, to.split('/')[0]), { recursive: true })
|
||||
renameSync(fromPath, toPath)
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
resolve: {
|
||||
alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }
|
||||
},
|
||||
base: command === 'build' ? '/auth/' : '/',
|
||||
base: '/',
|
||||
server: {
|
||||
port: 4403,
|
||||
fs: {
|
||||
allow: ['..']
|
||||
},
|
||||
proxy: {
|
||||
'/': {
|
||||
target: 'http://localhost:4402',
|
||||
bypass: (req) => {
|
||||
const url = req.url?.split('?')[0]
|
||||
|
||||
// Root and examples served by Vite
|
||||
if (url === '/' || url === '') return '/../examples/index.html'
|
||||
if (url === '/examples' || url === '/examples/') return '/../examples/index.html'
|
||||
if (url?.startsWith('/examples/')) return `/../examples${url.slice(9)}`
|
||||
|
||||
// Let other proxies handle their routes
|
||||
return null
|
||||
}
|
||||
},
|
||||
'/auth/': {
|
||||
target: 'http://localhost:4402',
|
||||
ws: true,
|
||||
bypass: (req) => {
|
||||
const url = req.url?.split('?')[0]
|
||||
if (url?.startsWith('/auth/assets/')) return url.slice(5)
|
||||
if (url === '/auth/api/restricted') return '/restricted/api/index.html'
|
||||
|
||||
const routes = { '': '/', host: '/host/index.html', admin: '/admin/', 'restricted-forward': '/restricted/forward/index.html', 'restricted-api': '/restricted/api/index.html' }
|
||||
for (const [path, target] of Object.entries(routes)) {
|
||||
if ([`/auth/${path}`, `/auth/${path}/`, `/${path}`, `/${path}/`].includes(url)) return target
|
||||
}
|
||||
|
||||
if (/^\/auth\/([a-z]+\.){4}[a-z]+\/?$|^\/([a-z]+\.){4}[a-z]+\/?$/.test(url)) return '/reset/index.html'
|
||||
// Backend handles /auth/api/* and /auth/ws/* (no bypass - let proxy handle)
|
||||
if (url?.startsWith('/auth/api/') || url?.startsWith('/auth/ws/')) return null
|
||||
// Vite serves all assets
|
||||
if (url?.startsWith('/auth/assets/')) return url
|
||||
// Vite serves main app routes
|
||||
if (url === '/auth' || url === '/auth/') return '/auth/index.html'
|
||||
if (url === '/auth/admin' || url === '/auth/admin/') return '/auth/admin/index.html'
|
||||
if (url === '/auth/restricted' || url === '/auth/restricted/') return '/auth/restricted/index.html'
|
||||
if (url?.startsWith('/auth/') && /^\/auth\/([a-z]+\.)+[a-z]+\/?/.test(url)) return "/int/reset/index.html"
|
||||
// Vite serves source files (for HMR and dev)
|
||||
if (url?.startsWith('/auth/') && /\.(js|vue|css|ts|jsx|tsx|json)$/.test(url)) return url
|
||||
return null
|
||||
}
|
||||
},
|
||||
'/int/': {
|
||||
target: 'http://localhost:4402',
|
||||
bypass: (req) => {
|
||||
const url = req.url?.split('?')[0]
|
||||
// Vite serves /int/ apps
|
||||
if (url === '/int/host' || url === '/int/host/') return '/int/host/index.html'
|
||||
if (url === '/int/reset' || url === '/int/reset/' || url?.match(/^\/int\/reset\/([a-z]+\.){4}[a-z]+\/?$/)) return '/int/reset/index.html'
|
||||
if (url === '/int/forward' || url === '/int/forward/') return '/int/forward/index.html'
|
||||
if (url?.startsWith('/int/')) return url
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,12 +97,23 @@ export default defineConfig(({ command }) => ({
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
index: resolve(__dirname, 'index.html'),
|
||||
admin: resolve(__dirname, 'admin/index.html'),
|
||||
reset: resolve(__dirname, 'reset/index.html'),
|
||||
'restricted-forward': resolve(__dirname, 'restricted/forward/index.html'),
|
||||
'restricted-api': resolve(__dirname, 'restricted/api/index.html'),
|
||||
host: resolve(__dirname, 'host/index.html')
|
||||
auth: resolve(__dirname, 'auth/index.html'),
|
||||
admin: resolve(__dirname, 'auth/admin/index.html'),
|
||||
restricted: resolve(__dirname, 'auth/restricted/index.html'),
|
||||
host: resolve(__dirname, 'int/host/index.html'),
|
||||
reset: resolve(__dirname, 'int/reset/index.html'),
|
||||
forward: resolve(__dirname, 'int/forward/index.html')
|
||||
},
|
||||
output: {
|
||||
entryFileNames: (chunkInfo) => {
|
||||
return 'auth/assets/[name]-[hash].js'
|
||||
},
|
||||
chunkFileNames: (chunkInfo) => {
|
||||
return 'auth/assets/[name]-[hash].js'
|
||||
},
|
||||
assetFileNames: (assetInfo) => {
|
||||
return 'auth/assets/[name]-[hash][extname]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user