Fix model images 404 under a subpath mount (withBase)
Model image URLs are root-relative (/api/models/image/...), so on an /ops subpath deploy the raw <img src> resolved to the server root and 404'd. Wrap every model-image src in withBase(): machine/printer/PC/network detail heroes, the models settings preview, and the machine-badge / asset-label print pages. withBase leaves external http(s)/data URLs untouched.
This commit is contained in:
@@ -131,6 +131,7 @@ import { useRoute } from 'vue-router'
|
|||||||
import JsBarcode from 'jsbarcode'
|
import JsBarcode from 'jsbarcode'
|
||||||
import { renderQrDataUrl } from './qrLogo'
|
import { renderQrDataUrl } from './qrLogo'
|
||||||
import { getSetting } from '@/utils/siteSettings'
|
import { getSetting } from '@/utils/siteSettings'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import {
|
import {
|
||||||
TYPE_CONFIG, hasLocationType, resolveDefaultEncodes,
|
TYPE_CONFIG, hasLocationType, resolveDefaultEncodes,
|
||||||
effectiveEncodes as effEncodes, resolveCodeText as resolveText, captionFor,
|
effectiveEncodes as effEncodes, resolveCodeText as resolveText, captionFor,
|
||||||
@@ -171,7 +172,7 @@ const cardTitle = computed(() => {
|
|||||||
|
|
||||||
const imageUrl = computed(() => {
|
const imageUrl = computed(() => {
|
||||||
const ext = extension.value
|
const ext = extension.value
|
||||||
return (ext && ext.imageurl) ? ext.imageurl : null
|
return (ext && ext.imageurl) ? withBase(ext.imageurl) : null
|
||||||
})
|
})
|
||||||
|
|
||||||
// Small identity table shown on the card.
|
// Small identity table shown on the card.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const modelName = computed(() => {
|
|||||||
const imageUrl = computed(() => {
|
const imageUrl = computed(() => {
|
||||||
if (!machine.value) return null
|
if (!machine.value) return null
|
||||||
const img = machine.value.machine?.imageurl
|
const img = machine.value.machine?.imageurl
|
||||||
if (img) return img.startsWith('/') ? img : `/images/models/machines/${img}`
|
if (img) return withBase(img.startsWith('/') ? img : `/images/models/machines/${img}`)
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Image</label>
|
<label>Image</label>
|
||||||
<div class="image-manage">
|
<div class="image-manage">
|
||||||
<img v-if="form.imageurl" :src="form.imageurl" alt="Model image" class="image-thumb" />
|
<img v-if="form.imageurl" :src="withBase(form.imageurl)" alt="Model image" class="image-thumb" />
|
||||||
<div class="image-actions">
|
<div class="image-actions">
|
||||||
<template v-if="editingModel">
|
<template v-if="editingModel">
|
||||||
<input
|
<input
|
||||||
@@ -209,6 +209,7 @@ import { modelsApi, vendorsApi, modeltypesApi } from '../../api'
|
|||||||
import PaginationBar from '../../components/PaginationBar.vue'
|
import PaginationBar from '../../components/PaginationBar.vue'
|
||||||
import { useToast } from '../../composables/toast'
|
import { useToast } from '../../composables/toast'
|
||||||
import { apiError } from '../../utils/apiError'
|
import { apiError } from '../../utils/apiError'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import { useListQuery } from '@/composables/listQuery'
|
import { useListQuery } from '@/composables/listQuery'
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<div class="hero-card">
|
<div class="hero-card">
|
||||||
<div class="hero-image" v-if="computer.computer?.imageurl">
|
<div class="hero-image" v-if="computer.computer?.imageurl">
|
||||||
<img :src="computer.computer.imageurl" :alt="computer.computer.modelname || 'Model photo'" />
|
<img :src="withBase(computer.computer.imageurl)" :alt="computer.computer.modelname || 'Model photo'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="hero-title">
|
<div class="hero-title">
|
||||||
@@ -241,6 +241,7 @@ import { ref, onMounted } from 'vue'
|
|||||||
import { colorStyle } from "@/utils/colorStyle"
|
import { colorStyle } from "@/utils/colorStyle"
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { computersApi, applicationsApi } from '@/api'
|
import { computersApi, applicationsApi } from '@/api'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import { useWarrantyBadge } from '@/composables/warrantyBadge'
|
import { useWarrantyBadge } from '@/composables/warrantyBadge'
|
||||||
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
||||||
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<div class="hero-card">
|
<div class="hero-card">
|
||||||
<div class="hero-image" v-if="machine.machine?.imageurl">
|
<div class="hero-image" v-if="machine.machine?.imageurl">
|
||||||
<img :src="machine.machine.imageurl" :alt="machine.machine.modelname || 'Model photo'" />
|
<img :src="withBase(machine.machine.imageurl)" :alt="machine.machine.modelname || 'Model photo'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="hero-title">
|
<div class="hero-title">
|
||||||
@@ -235,6 +235,7 @@
|
|||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { machinesApi } from '@/api'
|
import { machinesApi } from '@/api'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
||||||
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
||||||
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="detail-page" v-if="device">
|
<div class="detail-page" v-if="device">
|
||||||
<div class="hero-card">
|
<div class="hero-card">
|
||||||
<div class="hero-image">
|
<div class="hero-image">
|
||||||
<img v-if="device.networkdevice?.imageurl" :src="device.networkdevice.imageurl" alt="Model photo" />
|
<img v-if="device.networkdevice?.imageurl" :src="withBase(device.networkdevice.imageurl)" alt="Model photo" />
|
||||||
<div class="device-icon" v-else>
|
<div class="device-icon" v-else>
|
||||||
<span class="icon"><component :is="getDeviceIcon()" :size="24" /></span>
|
<span class="icon"><component :is="getDeviceIcon()" :size="24" /></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -202,6 +202,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import { Network, Router, Shield, Wifi, Camera, Server, Server as Rack, Globe } from 'lucide-vue-next'
|
import { Network, Router, Shield, Wifi, Camera, Server, Server as Rack, Globe } from 'lucide-vue-next'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { networkApi } from '@/api'
|
import { networkApi } from '@/api'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import AssetRelationships from '@/components/AssetRelationships.vue'
|
import AssetRelationships from '@/components/AssetRelationships.vue'
|
||||||
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
||||||
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<div class="hero-card">
|
<div class="hero-card">
|
||||||
<div class="hero-image" v-if="printer.printer?.imageurl">
|
<div class="hero-image" v-if="printer.printer?.imageurl">
|
||||||
<img :src="printer.printer.imageurl" :alt="printer.printer.modelname || 'Model photo'" />
|
<img :src="withBase(printer.printer.imageurl)" :alt="printer.printer.modelname || 'Model photo'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="hero-title">
|
<div class="hero-title">
|
||||||
@@ -280,6 +280,7 @@
|
|||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { printersApi } from '@/api'
|
import { printersApi } from '@/api'
|
||||||
|
import { withBase } from '@/utils/basePath'
|
||||||
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
import LocationMapTooltip from '@/components/LocationMapTooltip.vue'
|
||||||
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
import CustomFieldsSection from '@/components/CustomFieldsSection.vue'
|
||||||
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
||||||
|
|||||||
Reference in New Issue
Block a user