fix: webui works with baseUrl

This commit is contained in:
Gauthier Roebroeck 2020-02-20 12:30:16 +08:00
parent 6974f28706
commit bb183828a1
16 changed files with 156 additions and 37 deletions

View file

@ -9272,6 +9272,16 @@
"integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=",
"dev": true
},
"html-webpack-inject-attributes-plugin": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/html-webpack-inject-attributes-plugin/-/html-webpack-inject-attributes-plugin-1.0.2.tgz",
"integrity": "sha512-q+SueBjlEkkT9wg8F1uyQjprZRhT5JrEC7paT5oYsCUOOUxwIrk2Wa7VSTKK9DSUila+diHlevSR24s7whYjMA==",
"dev": true,
"requires": {
"lodash.assign": "^4.2.0",
"lodash.foreach": "^4.5.0"
}
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
@ -11058,12 +11068,24 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
"dev": true
},
"lodash.defaultsdeep": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
"integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==",
"dev": true
},
"lodash.foreach": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz",
"integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=",
"dev": true
},
"lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",

View file

@ -43,6 +43,7 @@
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"html-webpack-inject-attributes-plugin": "^1.0.2",
"sass": "^1.24.4",
"sass-loader": "^8.0.2",
"ts-jest": "^23.0.0",

View file

@ -4,8 +4,13 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Komga</title>
<link rel="icon" href="/favicon.ico" th:href="@{/favicon.ico}">
<script th:inline="javascript">
/*<![CDATA[*/
window.resourceBaseUrl = /*[(<%="$"%>{"'" + baseUrl + "'"})]*/ '/'
/*]]>*/
</script>
</head>
<body>
<noscript>

View file

@ -1,15 +1,27 @@
const baseURL = process.env.VUE_APP_KOMGA_API_URL ? process.env.VUE_APP_KOMGA_API_URL : window.location.origin
const fullUrl = process.env.VUE_APP_KOMGA_API_URL
? process.env.VUE_APP_KOMGA_API_URL
: window.location.origin + window.resourceBaseUrl
const baseUrl = process.env.NODE_ENV === 'production' ? window.resourceBaseUrl : '/'
const urls = {
origin: !fullUrl.endsWith('/') ? `${fullUrl}/` : fullUrl,
originNoSlash: fullUrl.endsWith('/') ? fullUrl.slice(0, -1) : fullUrl,
base: !baseUrl.endsWith('/') ? `${baseUrl}/` : baseUrl,
baseNoSlash: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl
} as Urls
export default urls
export function bookThumbnailUrl (bookId: number): string {
return `${baseURL}/api/v1/books/${bookId}/thumbnail`
return `${urls.originNoSlash}/api/v1/books/${bookId}/thumbnail`
}
export function bookFileUrl (bookId: number): string {
return `${baseURL}/api/v1/books/${bookId}/file`
return `${urls.originNoSlash}/api/v1/books/${bookId}/file`
}
export function bookPageUrl (bookId: number, page: number, convertTo?: string): string {
let url = `${baseURL}/api/v1/books/${bookId}/pages/${page}`
let url = `${urls.originNoSlash}/api/v1/books/${bookId}/pages/${page}`
if (convertTo) {
url += `?convert=${convertTo}`
}
@ -17,9 +29,9 @@ export function bookPageUrl (bookId: number, page: number, convertTo?: string):
}
export function bookPageThumbnailUrl (bookId: number, page: number): string {
return `${baseURL}/api/v1/books/${bookId}/pages/${page}/thumbnail`
return `${urls.originNoSlash}/api/v1/books/${bookId}/pages/${page}/thumbnail`
}
export function seriesThumbnailUrl (seriesId: number): string {
return `${baseURL}/api/v1/series/${seriesId}/thumbnail`
return `${urls.originNoSlash}/api/v1/series/${seriesId}/thumbnail`
}

View file

@ -1,3 +1,4 @@
import './public-path'
import _, { LoDashStatic } from 'lodash'
import Vue from 'vue'
import VueCookies from 'vue-cookies'
@ -47,3 +48,9 @@ declare module 'vue/types/vue' {
$_: LoDashStatic;
}
}
declare global {
interface Window {
resourceBaseUrl: string
}
}

View file

@ -1,10 +1,11 @@
import urls from '@/functions/urls'
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import _Vue from 'vue'
export default {
install (Vue: typeof _Vue) {
Vue.prototype.$http = axios.create({
baseURL: process.env.VUE_APP_KOMGA_API_URL ? process.env.VUE_APP_KOMGA_API_URL : window.location.origin,
baseURL: urls.origin,
withCredentials: true,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
} as AxiosRequestConfig)

View file

@ -0,0 +1,2 @@
// eslint-disable-next-line camelcase
__webpack_public_path__ = process.env.NODE_ENV === 'production' ? window.location.origin + window.resourceBaseUrl : '/'

View file

@ -1,3 +1,4 @@
import urls from '@/functions/urls'
import Vue from 'vue'
import Router from 'vue-router'
import store from './store'
@ -13,7 +14,7 @@ const adminGuard = (to: any, from: any, next: any) => {
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
base: urls.base,
routes: [
{
path: '/',

View file

@ -0,0 +1,6 @@
interface Urls {
origin: string,
originNoSlash: string,
base: string,
baseNoSlash: string
}

33
komga-webui/vue.config.js Normal file
View file

@ -0,0 +1,33 @@
const htmlInject = require('html-webpack-inject-attributes-plugin')
const _ = require('lodash')
// vue.config.js
module.exports = {
publicPath: '/',
chainWebpack: (config) => {
config.plugins.delete('prefetch') // conflicts with htmlInject
config.plugins.delete('preload') // conflicts with htmlInject
config.plugin('html')
.tap(args => {
args[0].attributes = {
'th:href': function (tag) {
if (_.has(tag, 'attributes.href')) {
return `@{${tag.attributes.href}}`
}
return false
},
'th:src': function (tag) {
if (_.has(tag, 'attributes.src')) {
return `@{${tag.attributes.src}}`
}
return false
}
}
return args
})
config.plugin('html-inject')
.after('html')
.use(htmlInject)
}
}

View file

@ -39,6 +39,7 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
kapt("org.springframework.boot:spring-boot-configuration-processor")

View file

@ -1,18 +0,0 @@
package org.gotson.komga.infrastructure.web
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.servlet.ModelAndView
import javax.servlet.http.HttpServletRequest
@Component
class SPAErrorViewResolver : ErrorViewResolver {
override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus, model: MutableMap<String, Any>): ModelAndView? =
when {
request.requestURL.toString() == "/error" -> null
status == HttpStatus.NOT_FOUND -> ModelAndView("/", HttpStatus.TEMPORARY_REDIRECT)
else -> ModelAndView("/error", status)
}
}

View file

@ -1,22 +1,42 @@
package org.gotson.komga.infrastructure.web
import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.context.annotation.Configuration
import org.springframework.http.CacheControl
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class StaticResourceConfiguration : WebMvcConfigurer {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry//.setOrder(Ordered.HIGHEST_PRECEDENCE)
.addResourceHandler(
"/index.html",
"/favicon.ico"
)
.addResourceLocations(
"classpath:public/index.html",
"classpath:public/favicon.ico"
)
.setCacheControl(CacheControl.noStore())
.addResourceHandler(
"/index.html",
"/favicon.ico"
)
.addResourceLocations(
"classpath:public/index.html",
"classpath:public/favicon.ico"
)
.setCacheControl(CacheControl.noStore())
}
override fun addViewControllers(registry: ViewControllerRegistry) {
registry.addViewController("/notFound")
.setStatusCode(HttpStatus.OK)
.setViewName("forward:/")
}
}
@Component
class CustomContainer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
override fun customize(factory: ConfigurableServletWebServerFactory) {
factory.addErrorPages(ErrorPage(HttpStatus.NOT_FOUND, "/notFound"))
}
}

View file

@ -0,0 +1,21 @@
package org.gotson.komga.interfaces.mvc
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import javax.servlet.ServletContext
@Controller
class IndexController(
servletContext: ServletContext
) {
private val baseUrl: String =
if (servletContext.contextPath.isBlank()) "/"
else "${servletContext.contextPath}/"
@GetMapping("/")
fun index(model: Model): String {
model.addAttribute("baseUrl", baseUrl)
return "index"
}
}

View file

@ -35,3 +35,7 @@ logging:
management.metrics.export.influx:
# enabled: true
uri: http://localhost:8086
#server:
# servlet:
# context-path: /komga

View file

@ -22,7 +22,8 @@ spring:
use_second_level_cache: true
use_query_cache: true
region.factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
thymeleaf:
prefix: classpath:/public/
server.servlet.session.timeout: 7d
management: