Feature: use nuxt bridge to bundle client

This commit is contained in:
Aram Becker 2023-05-04 23:52:36 +02:00
parent 98c117f0b8
commit 228b5203a8
23 changed files with 8484 additions and 19783 deletions

View file

@ -14,6 +14,7 @@ npm-debug.log
dev.js
test/
/client/.nuxt/
/client/.output/
/client/dist/
/dist/
/build/

1
.gitignore vendored
View file

@ -9,6 +9,7 @@
/metadata/
test/
/client/.nuxt/
/client/.output/
/client/dist/
/dist/
/deploy/

View file

@ -23,16 +23,12 @@ RUN apk update && \
COPY --from=tone /usr/local/bin/tone /usr/local/bin/
COPY package* ./
COPY --from=build /client/package*.json client/
RUN npm ci --omit=dev
RUN cd client && npm ci --omit=dev && cd ..
COPY index.js package* ./
COPY server server
COPY --from=build /client/nuxt.config.js* client/
COPY --from=build /client/.nuxt client/.nuxt
COPY --from=build /client/modules client/modules
COPY --from=build /client/.output ./client/.output
EXPOSE 80
HEALTHCHECK \

View file

@ -10,12 +10,12 @@
<div v-show="routeName === route.iod" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.version" />
</div>
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.public.version" />
</div>
<div class="w-44 h-12 px-4 border-t bg-bg border-black border-opacity-20 fixed left-0 flex flex-col justify-center" :class="wrapperClass" :style="{ bottom: streamLibraryItem ? '160px' : '0px' }">
<div class="flex justify-between">
<p class="underline font-mono text-sm" @click="clickChangelog">v{{ $config.version }}</p>
<div class="w-44 h-12 px-4 border-t bg-bg border-black border-opacity-20 fixed left-0 flex flex-col justify-center" :class="wrapperClass" :style="{ bottom: streamLibraryItem ? '160px' : '0px' }">
<div class="flex justify-between">
<p class="underline font-mono text-sm" @click="clickChangelog">v{{ $config.public.version }}</p>
<p class="font-mono text-xs text-gray-300 italic">{{ Source }}</p>
</div>

View file

@ -114,12 +114,12 @@
</nuxt-link>
<div class="w-full h-12 px-1 py-2 border-t border-black border-opacity-20 absolute left-0" :style="{ bottom: streamLibraryItem ? '240px' : '65px' }">
<p class="underline font-mono text-xs text-center text-gray-300 leading-3 mb-1" @click="clickChangelog">v{{ $config.version }}</p>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xxs text-center block leading-3">Update</a>
<p v-else class="text-xxs text-gray-400 leading-3 text-center italic">{{ Source }}</p>
</div>
<p class="underline font-mono text-xs text-center text-gray-300 leading-3 mb-1" @click="clickChangelog">v{{ $config.public.version }}</p>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xxs text-center block leading-3">Update</a>
<p v-else class="text-xxs text-gray-400 leading-3 text-center italic">{{ Source }}</p>
</div>
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.version" />
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.public.version" />
</div>
</template>

View file

@ -58,7 +58,7 @@ export default {
if (!this.imagePath) return null
if (process.env.NODE_ENV !== 'production') {
// Testing
return `http://localhost:3333${this.$config.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
return `http://localhost:3333${this.$config.public.routerBasePath}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}
return `/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
}

View file

@ -55,7 +55,7 @@ export default {
return this.item.coverPath
},
coverUrl() {
if (!this.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg`
if (!this.coverPath) return `${this.$config.public.routerBasePath}/book_placeholder.jpg`
return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId)
},
bookCoverAspectRatio() {

View file

@ -137,7 +137,7 @@ export default {
slug: this.newFeedSlug,
metadataDetails: this.metadataDetails
}
if (this.$isDev) payload.serverAddress = `http://localhost:3333${this.$config.routerBasePath}`
if (this.$isDev) payload.serverAddress = `http://localhost:3333${this.$config.public.routerBasePath}`
console.log('Payload', payload)
this.$axios

View file

@ -1,26 +1,26 @@
<template>
<div class="h-full w-full">
<div class="h-full flex items-center justify-center">
<div style="width: 100px; max-width: 100px" class="h-full hidden sm:flex items-center overflow-x-hidden justify-center">
<span v-if="hasPrev" class="material-icons text-white text-opacity-50 hover:text-opacity-80 cursor-pointer text-6xl" @mousedown.prevent @click="prev">chevron_left</span>
<div class="w-full h-full">
<div class="flex items-center justify-center h-full">
<div style="width: 100px; max-width: 100px" class="items-center justify-center hidden h-full overflow-x-hidden sm:flex">
<span v-if="hasPrev" class="text-6xl text-white text-opacity-50 cursor-pointer material-icons hover:text-opacity-80" @mousedown.prevent @click="prev">chevron_left</span>
</div>
<div id="frame" class="w-full" style="height: 80%">
<div id="viewer"></div>
</div>
<div style="width: 100px; max-width: 100px" class="h-full hidden sm:flex items-center justify-center overflow-x-hidden">
<span v-if="hasNext" class="material-icons text-white text-opacity-50 hover:text-opacity-80 cursor-pointer text-6xl" @mousedown.prevent @click="next">chevron_right</span>
<div style="width: 100px; max-width: 100px" class="items-center justify-center hidden h-full overflow-x-hidden sm:flex">
<span v-if="hasNext" class="text-6xl text-white text-opacity-50 cursor-pointer material-icons hover:text-opacity-80" @mousedown.prevent @click="next">chevron_right</span>
</div>
</div>
</div>
</template>
<script>
import ePub from 'epubjs'
import { Epub, Rendition, NavItem } from 'epubjs'
/**
* @typedef {object} EpubReader
* @property {ePub.Book} book
* @property {ePub.Rendition} rendition
* @property {Epub} book
* @property {Rendition} rendition
*/
export default {
props: {
@ -33,9 +33,9 @@ export default {
data() {
return {
windowWidth: 0,
/** @type {ePub.Book} */
/** @type {Epub} */
book: null,
/** @type {ePub.Rendition} */
/** @type {Rendition} */
rendition: null
}
},
@ -50,7 +50,7 @@ export default {
hasNext() {
return !this.rendition?.location?.atEnd
},
/** @returns {Array<ePub.NavItem>} */
/** @returns {Array<NavItem>} */
chapters() {
return this.book ? this.book.navigation.toc : []
},
@ -200,13 +200,13 @@ export default {
/** @type {EpubReader} */
const reader = this
/** @type {ePub.Book} */
/** @type {Epub} */
reader.book = new ePub(reader.url, {
width: this.readerWidth,
height: window.innerHeight - 50
})
/** @type {ePub.Rendition} */
/** @type {Rendition} */
reader.rendition = reader.book.renderTo('viewer', {
width: this.readerWidth,
height: window.innerHeight * 0.8

View file

@ -368,7 +368,7 @@ export default {
initializeSocket() {
this.socket = this.$nuxtSocket({
name: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
channel: this.$config.routerBasePath,
channel: this.$config.public.routerBasePath,
persist: 'main',
teardown: false,
transports: ['websocket'],

17
client/nitro.entry.js Normal file
View file

@ -0,0 +1,17 @@
import '#internal/nitro/virtual/polyfill'
const nitroApp = useNitroApp()
const listener = toNodeListener(nitroApp.h3App)
const handler = listener
{
process.on(
"unhandledRejection",
(err) => console.error("[nitro] [dev] [unhandledRejection] " + err)
);
process.on(
"uncaughtException",
(err) => console.error("[nitro] [dev] [uncaughtException] " + err)
);
}
export { useRuntimeConfig, getRouteRules, handler, listener, useNitroApp };

7
client/nitro.preset.js Normal file
View file

@ -0,0 +1,7 @@
import { defineNitroPreset } from 'nitropack'
import { fileURLToPath } from 'node:url'
export default defineNitroPreset({
extends: 'node-server',
entry: fileURLToPath(new URL('./nitro.entry.js', import.meta.url))
})

View file

@ -1,6 +1,8 @@
const { defineNuxtConfig } = require('@nuxt/bridge')
import { fromNodeMiddleware } from 'h3';
const pkg = require('./package.json')
module.exports = ({ command }) => ({
module.exports = defineNuxtConfig({
// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
ssr: false,
target: 'server',
@ -11,9 +13,11 @@ module.exports = ({ command }) => ({
},
telemetry: false,
publicRuntimeConfig: {
version: pkg.version,
routerBasePath: process.env.ROUTER_BASE_PATH || ''
runtimeConfig: {
public: {
version: pkg.version,
routerBasePath: process.env.ROUTER_BASE_PATH || ''
}
},
// Global page headers: https://go.nuxtjs.dev/config-head
@ -31,7 +35,7 @@ module.exports = ({ command }) => ({
router: {
// We must specify `./` during build to support dynamic router base paths (https://github.com/nuxt/nuxt/issues/10088)
base: command == 'build' ? './' : process.env.ROUTER_BASE_PATH || ''
base: process.env.ROUTER_BASE_PATH || ''
},
// Global CSS: https://go.nuxtjs.dev/config-css
@ -59,31 +63,16 @@ module.exports = ({ command }) => ({
buildModules: [
// https://go.nuxtjs.dev/tailwindcss
'@nuxtjs/tailwindcss',
'@nuxtjs/pwa',
'@nuxt/postcss8'
'pwa-nuxt-bridge',
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'nuxt-socket-io',
'@nuxtjs/axios',
'@nuxtjs/proxy',
'@/modules/rewritePwaManifest.js'
// '@/modules/rewritePwaManifest.js'
],
proxy: {
[`${process.env.ROUTER_BASE_PATH || ''}/dev/`]: {
target: `http://localhost:3333${process.env.ROUTER_BASE_PATH || ''}`,
pathRewrite: { [`^${process.env.ROUTER_BASE_PATH || ''}/dev/`]: process.env.ROUTER_BASE_PATH || '' }
},
[`${process.env.ROUTER_BASE_PATH || ''}/ebook/`]: {
target: (process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '') + `${process.env.ROUTER_BASE_PATH || ''}/`
},
[`${process.env.ROUTER_BASE_PATH || ''}/s/`]: {
target: (process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '') + `${process.env.ROUTER_BASE_PATH || ''}/`
}
},
io: {
sockets: [{
name: 'dev',
@ -138,9 +127,11 @@ module.exports = ({ command }) => ({
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {},
postcssOptions: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
}
},
@ -155,6 +146,34 @@ module.exports = ({ command }) => ({
host: '0.0.0.0'
},
bridge: {
capi: false,
scriptSetup: false
},
nitro: {
preset: './nitro.preset.js',
devProxy: {
[`${process.env.ROUTER_BASE_PATH || ''}/dev/`]: {
target: `http://localhost:3333${process.env.ROUTER_BASE_PATH || ''}`,
pathRewrite: { [`^${process.env.ROUTER_BASE_PATH || ''}/dev/`]: process.env.ROUTER_BASE_PATH || '' }
},
[`${process.env.ROUTER_BASE_PATH || ''}/ebook/`]: {
target: (process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '') + `${process.env.ROUTER_BASE_PATH || ''}/`
},
[`${process.env.ROUTER_BASE_PATH || ''}/s/`]: {
target: (process.env.NODE_ENV !== 'production' ? 'http://localhost:3333' : '') + `${process.env.ROUTER_BASE_PATH || ''}/`
}
}
},
hooks: {
// 'nitro:config': (config) => {
// console.log('nitro:config', config)
// process.exit(0)
// },
},
/**
* Temporary workaround for @nuxt-community/tailwindcss-module.
*

28061
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,26 +4,26 @@
"description": "Self-hosted audiobook and podcast client",
"main": "index.js",
"scripts": {
"dev": "nuxt",
"dev2": "nuxt --hostname localhost --port 1337",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
"dev": "nuxi dev",
"dev2": "nuxi dev --hostname localhost --port 1337",
"build": "nuxi build",
"start": "nuxi preview",
"generate": "nuxi generate"
},
"author": "advplyr",
"license": "ISC",
"dependencies": {
"@nuxt/bridge": "npm:@nuxt/bridge-edge@^3.0.0-28051151.e88ad64",
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/proxy": "^2.1.0",
"core-js": "^3.16.0",
"cron-parser": "^4.7.1",
"date-fns": "^2.25.0",
"epubjs": "^0.3.88",
"glob": "^10.2.2",
"epubjs": "^0.5.0-alpha.0",
"hls.js": "^1.0.7",
"libarchive.js": "^1.3.0",
"nuxt": "^2.15.8",
"nuxt": "^2.16.3",
"nuxt-socket-io": "^1.1.18",
"pwa-nuxt-bridge": "^5.0.1",
"trix": "^1.3.1",
"v-click-outside": "^3.1.2",
"vue-pdf": "^4.2.0",
@ -31,10 +31,9 @@
"vuedraggable": "^2.24.3"
},
"devDependencies": {
"@nuxt/postcss8": "^1.1.3",
"@nuxtjs/pwa": "^3.3.5",
"@nuxtjs/tailwindcss": "^4.2.1",
"@nuxtjs/tailwindcss": "^6.6.7",
"autoprefixer": "^10.4.7",
"minimatch": "^8.0.4",
"postcss": "^8.3.6",
"tailwindcss": "^3.1.4"
}

View file

@ -415,7 +415,7 @@ export default {
const audioEl = this.audioEl || document.createElement('audio')
var src = audioTrack.contentUrl + `?token=${this.userToken}`
if (this.$isDev) {
src = `http://localhost:3333${this.$config.routerBasePath}${src}`
src = `http://localhost:3333${this.$config.public.routerBasePath}${src}`
}
audioEl.src = src

View file

@ -18,8 +18,8 @@ export default function ({ $axios, store, $config }) {
}
console.log($config)
if ($config.routerBasePath) {
config.url = `${$config.routerBasePath}${config.url}`
if ($config.public.routerBasePath) {
config.url = `${$config.public.routerBasePath}${config.url}`
}
})

View file

@ -4,7 +4,7 @@ export default function ({ $config }) {
const link = document.createElement('link')
link.rel = 'icon'
link.type = 'image/x-icon'
link.href = `${$config.routerBasePath || ''}${faviconPath}`
link.href = `${$config.public.routerBasePath || ''}${faviconPath}`
document.head.appendChild(link)
}

View file

@ -182,5 +182,5 @@ export default ({ app, store }, inject) => {
inject('eventBus', new Vue())
inject('isDev', process.env.NODE_ENV !== 'production')
store.commit('setRouterBasePath', app.$config.routerBasePath)
store.commit('setRouterBasePath', app.$config.public.routerBasePath)
}

1
package-lock.json generated
View file

@ -13,6 +13,7 @@
"express": "^4.17.1",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"mime-types": "^2.1.35",
"node-tone": "^1.0.1",
"socket.io": "^4.5.4",
"xml2js": "^0.5.0"

View file

@ -39,6 +39,7 @@
"express": "^4.17.1",
"graceful-fs": "^4.2.10",
"htmlparser2": "^8.0.1",
"mime-types": "^2.1.35",
"node-tone": "^1.0.1",
"socket.io": "^4.5.4",
"xml2js": "^0.5.0"
@ -46,4 +47,4 @@
"devDependencies": {
"nodemon": "^2.0.20"
}
}
}

View file

@ -139,7 +139,7 @@ class Server {
Logger.info('=== Starting Server ===')
await this.init()
if (process.env.NODE_ENV !== 'development') {
this.clientRouter.start()
await this.clientRouter.start()
}
const app = express()

View file

@ -1,4 +1,5 @@
const express = require('express')
const mime = require('mime-types')
const Path = require('path')
const Logger = require('../Logger')
@ -14,18 +15,15 @@ class ClientRouter {
async init () {
const clientDir = Path.join(this.appRoot, '/client')
const { loadNuxt } = require(Path.resolve(clientDir, 'node_modules/nuxt'))
this.client = await loadNuxt({ rootDir: clientDir, for: 'start' })
const { handler } = await import(Path.join(clientDir, '.output/server/index.mjs'))
this.client = handler
}
async start () {
Logger.info('[Client] Starting')
await this.init()
this.router.use(this.client.render)
this.client.hook('error', (err) => Logger.error('[Client]', err))
this.client.ready().then(() => Logger.info('[Client] Ready'))
this.router.use('/', this.client)
}
async stop () {