add service worker

This commit is contained in:
jxxghp
2024-06-05 18:12:07 +08:00
parent da910ac670
commit 157c37c862
16 changed files with 1044 additions and 715 deletions
+1
View File
@@ -1 +1,2 @@
VITE_API_BASE_URL=http://localhost:3001/api/v1/ VITE_API_BASE_URL=http://localhost:3001/api/v1/
VITE_PUBLIC_VAPID_KEY=BH3w49sZA6jXUnE-yt4jO6VKh73lsdsvwoJ6Hx7fmPIDKoqGiUl2GEoZzy-iJfn4SfQQcx7yQdHf9RknwrL_lSM
+1
View File
@@ -1 +1,2 @@
VITE_API_BASE_URL=api/v1/ VITE_API_BASE_URL=api/v1/
VITE_PUBLIC_VAPID_KEY=BH3w49sZA6jXUnE-yt4jO6VKh73lsdsvwoJ6Hx7fmPIDKoqGiUl2GEoZzy-iJfn4SfQQcx7yQdHf9RknwrL_lSM
+1
View File
@@ -11,6 +11,7 @@ node_modules
.DS_Store .DS_Store
dist dist
dist-ssr dist-ssr
dev-dist
*.local *.local
/cypress/videos/ /cypress/videos/
-1
View File
@@ -15,7 +15,6 @@
<link rel="apple-touch-icon" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="apple-touch-startup-image" href="/splash/apple-splash.jpg" /> <link rel="apple-touch-startup-image" href="/splash/apple-splash.jpg" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="manifest.json" crossorigin="use-credentials" />
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "moviepilot", "name": "moviepilot",
"version": "1.9.3-4", "version": "1.9.4-beta",
"private": true, "private": true,
"bin": "dist/service.js", "bin": "dist/service.js",
"scripts": { "scripts": {
@@ -42,7 +42,6 @@
"sass": "^1.59.3", "sass": "^1.59.3",
"tailwindcss": "^3.3.2", "tailwindcss": "^3.3.2",
"unplugin-vue-define-options": "^1.3.5", "unplugin-vue-define-options": "^1.3.5",
"vite-plugin-pwa": "^0.19.8",
"vue": "^3.3.2", "vue": "^3.3.2",
"vue-router": "^4.2.0", "vue-router": "^4.2.0",
"vue-toast-notification": "^3", "vue-toast-notification": "^3",
@@ -92,6 +91,7 @@
"unplugin-vue-components": "^0.26.0", "unplugin-vue-components": "^0.26.0",
"vite": "^5.2.8", "vite": "^5.2.8",
"vite-plugin-pages": "^0.32.1", "vite-plugin-pages": "^0.32.1",
"vite-plugin-pwa": "^0.20.0",
"vite-plugin-vue-layouts": "^0.11.0", "vite-plugin-vue-layouts": "^0.11.0",
"vite-plugin-vuetify": "2.0.3", "vite-plugin-vuetify": "2.0.3",
"vue-shepherd": "^3.0.0", "vue-shepherd": "^3.0.0",
-80
View File
@@ -1,80 +0,0 @@
{
"name": "MoviePilot",
"short_name": "MoviePilot",
"start_url": "./",
"display": "standalone",
"icons": [
{
"src": "./android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "./android-chrome-192x192_maskable.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "./android-chrome-512x512_maskable.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#28243D",
"background_color": "#28243D",
"shortcuts": [
{
"name": "推荐",
"url": "./ranking",
"icons": [
{
"src": "./sparkles-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "电影订阅",
"url": "./subscribe-movie",
"icons": [
{
"src": "./clock-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "电视剧订阅",
"url": "./subscribe-tv",
"icons": [
{
"src": "./clock-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
},
{
"name": "设置",
"url": "./setting",
"icons": [
{
"src": "./cog-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
]
}
+2
View File
@@ -0,0 +1,2 @@
User-agent: *
Disallow: /
+10 -10
View File
@@ -5,14 +5,14 @@
/** /**
* 修复低版本Safari等浏览器数组不支持at函数的问题 * 修复低版本Safari等浏览器数组不支持at函数的问题
*/ */
export function fixArrayAt() { ;(function fixArrayAt() {
if (!Array.prototype.at) { if (!Array.prototype.at) {
Array.prototype.at = function(index: number) { Array.prototype.at = function (index: number) {
if (index >= 0) { if (index >= 0) {
return this[index] return this[index]
} else { } else {
return this[this.length + index] return this[this.length + index]
} }
}
} }
} }
})()
+14
View File
@@ -28,3 +28,17 @@ export async function copyToClipboard(content: string) {
document.body.removeChild(input) document.body.removeChild(input)
} }
} }
// VAPID公钥转Uint8Array
export function urlBase64ToUint8Array(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
const rawData = window.atob(base64)
const outputArray = new Uint8Array(rawData.length)
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i)
}
return outputArray
}
+14 -20
View File
@@ -1,23 +1,19 @@
import { VAceEditor } from 'vue3-ace-editor' import '@/@core/utils/compatibility'
import { createApp } from 'vue'
import '@/@iconify/icons-bundle'
import ToastPlugin from 'vue-toast-notification'
import VuetifyUseDialog from 'vuetify-use-dialog'
import './ace-config' import './ace-config'
import VueApexCharts from 'vue3-apexcharts' import '@/@iconify/icons-bundle'
import { removeEl } from './@core/utils/dom' import '@/plugins/webfontloader'
import App from '@/App.vue' import App from '@/App.vue'
import vuetify from '@/plugins/vuetify' import vuetify from '@/plugins/vuetify'
import { loadFonts } from '@/plugins/webfontloader'
import router from '@/router' import router from '@/router'
import store from '@/store' import store from '@/store'
import '@core/scss/template/index.scss' import { VAceEditor } from 'vue3-ace-editor'
import '@layouts/styles/index.scss' import { createApp } from 'vue'
import '@styles/styles.scss' import { removeEl } from './@core/utils/dom'
import 'vue-toast-notification/dist/theme-bootstrap.css'
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar' import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import 'vue3-perfect-scrollbar/style.css'
import { VTreeview } from 'vuetify/labs/VTreeview' import { VTreeview } from 'vuetify/labs/VTreeview'
import ToastPlugin from 'vue-toast-notification'
import VuetifyUseDialog from 'vuetify-use-dialog'
import VueApexCharts from 'vue3-apexcharts'
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue' import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
import MediaCard from './components/cards/MediaCard.vue' import MediaCard from './components/cards/MediaCard.vue'
import PosterCard from './components/cards/PosterCard.vue' import PosterCard from './components/cards/PosterCard.vue'
@@ -27,13 +23,11 @@ import MediaInfoCard from './components/cards/MediaInfoCard.vue'
import TorrentCard from './components/cards/TorrentCard.vue' import TorrentCard from './components/cards/TorrentCard.vue'
import MediaIdSelector from './components/misc/MediaIdSelector.vue' import MediaIdSelector from './components/misc/MediaIdSelector.vue'
import PathField from './components/input/PathField.vue' import PathField from './components/input/PathField.vue'
import { fixArrayAt } from '@/@core/utils/compatibility' import '@core/scss/template/index.scss'
import '@layouts/styles/index.scss'
// 修复低版本Safari等浏览器数组不支持at函数的问题 import '@styles/styles.scss'
fixArrayAt() import 'vue-toast-notification/dist/theme-bootstrap.css'
import 'vue3-perfect-scrollbar/style.css'
// 加载字体
loadFonts()
// 创建Vue实例 // 创建Vue实例
const app = createApp(App) const app = createApp(App)
+25
View File
@@ -8,6 +8,7 @@ import router from '@/router'
import logo from '@images/logo.png' import logo from '@images/logo.png'
import { useTheme } from 'vuetify' import { useTheme } from 'vuetify'
import { checkPrefersColorSchemeIsDark } from '@/@core/utils' import { checkPrefersColorSchemeIsDark } from '@/@core/utils'
import { urlBase64ToUint8Array } from '@/@core/utils/navigator'
const { global: globalTheme } = useTheme() const { global: globalTheme } = useTheme()
@@ -89,9 +90,33 @@ async function setTheme() {
localStorage.setItem('materio-initial-loader-bg', globalTheme.current.value.colors.background) localStorage.setItem('materio-initial-loader-bg', globalTheme.current.value.colors.background)
} }
// 订阅推送通知
async function subscribeForPushNotifications() {
if ('serviceWorker' in navigator && 'PushManager' in window) {
const registration = await navigator.serviceWorker.ready
// 获取订阅信息
const subscription = await registration.pushManager.getSubscription().then(function (subscription) {
if (subscription === null) {
const convertedVapidKey = urlBase64ToUint8Array(import.meta.env.VITE_PUBLIC_VAPID_KEY)
return registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey,
})
} else {
return subscription
}
})
// 发送订阅请求
await api.post('/message/subscribe', subscription)
}
}
// 登录后处理
async function afterLogin() { async function afterLogin() {
// 生效主题配置 // 生效主题配置
await setTheme() await setTheme()
// 订阅推送通知
await subscribeForPushNotifications()
// 跳转到首页或回原始页面 // 跳转到首页或回原始页面
router.push(store.state.auth.originalPath ?? '/') router.push(store.state.auth.originalPath ?? '/')
} }
+3 -3
View File
@@ -4,12 +4,12 @@
* webfontloader documentation: https://github.com/typekit/webfontloader * webfontloader documentation: https://github.com/typekit/webfontloader
*/ */
export async function loadFonts() { ;(async function loadFonts() {
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') const webFontLoader = await import(/* webpackChunkName: "webfontloader" */ 'webfontloader')
webFontLoader.load({ webFontLoader.load({
google: { google: {
families: ['Inter:100,200,300,400,500,600,700&display=swap'], families: ['Inter:100,200,300,400,500,600,700&display=swap'],
}, },
}) })
} })()
+39
View File
@@ -0,0 +1,39 @@
import { createHandlerBoundToURL, cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
import { NavigationRoute, registerRoute } from 'workbox-routing'
import { clientsClaim } from 'workbox-core'
declare let self: ServiceWorkerGlobalScope
cleanupOutdatedCaches()
// self.__WB_MANIFEST is default injection point
precacheAndRoute(self.__WB_MANIFEST)
// to allow work offline
registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'), { denylist: [/^\/api/] }))
// 通知选项
const options = {
icon: '/logo.png',
}
// 监听 push 事件,显示通知
self.addEventListener('push', function (e) {
if (!e.data) {
return
}
// 解析获取推送消息
let payload = e.data.json()
// 根据推送消息生成桌面通知并展现出来
let promise = self.registration.showNotification(payload.title, {
body: payload.body,
icon: payload.icon ?? options.icon,
data: {
url: payload.url,
},
})
e.waitUntil(promise)
})
self.skipWaiting()
clientsClaim()
+6 -3
View File
@@ -46,7 +46,8 @@
"esnext", "esnext",
"dom", "dom",
"dom.iterable", "dom.iterable",
"scripthost" "scripthost",
"WebWorker"
], ],
"skipLibCheck": true, "skipLibCheck": true,
"types": [ "types": [
@@ -63,11 +64,13 @@
"src/**/*.vue", "src/**/*.vue",
"themeConfig.ts", "themeConfig.ts",
"auto-imports.d.ts", "auto-imports.d.ts",
"components.d.ts" "components.d.ts",
"src/service-worker.ts",
"public/service.js"
], ],
"exclude": [ "exclude": [
"dist", "dist",
"node_modules", "node_modules",
"src/@iconify/*" "src/@iconify/*"
] ]
} }
+94 -10
View File
@@ -4,8 +4,8 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite' import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa'
import vuetify from 'vite-plugin-vuetify' import vuetify from 'vite-plugin-vuetify'
import { VitePWA } from 'vite-plugin-pwa'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@@ -13,8 +13,6 @@ export default defineConfig({
plugins: [ plugins: [
vue(), vue(),
vueJsx(), vueJsx(),
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
vuetify({ vuetify({
styles: { styles: {
configFile: 'src/styles/variables/_vuetify.scss', configFile: 'src/styles/variables/_vuetify.scss',
@@ -29,12 +27,100 @@ export default defineConfig({
vueTemplate: true, vueTemplate: true,
}), }),
VitePWA({ VitePWA({
registerType: 'autoUpdate',
injectRegister: 'script', injectRegister: 'script',
manifest: false, registerType: 'autoUpdate',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'service-worker.ts',
workbox: { workbox: {
navigateFallbackDenylist: [ globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg}'],
/.*\/api\/v\d+\/system\/logging.*/, navigateFallbackDenylist: [/.*\/api\/v\d+\/system\/logging.*/],
},
injectManifest: {
rollupFormat: 'iife',
},
devOptions: {
enabled: true,
type: 'module',
},
manifest: {
'name': 'MoviePilot',
'short_name': 'MoviePilot',
'start_url': './',
'display': 'standalone',
'icons': [
{
'src': './android-chrome-192x192.png',
'sizes': '192x192',
'type': 'image/png',
'purpose': 'any',
},
{
'src': './android-chrome-192x192_maskable.png',
'sizes': '192x192',
'type': 'image/png',
'purpose': 'maskable',
},
{
'src': './android-chrome-512x512.png',
'sizes': '512x512',
'type': 'image/png',
'purpose': 'any',
},
{
'src': './android-chrome-512x512_maskable.png',
'sizes': '512x512',
'type': 'image/png',
'purpose': 'maskable',
},
],
'theme_color': '#28243D',
'background_color': '#28243D',
'shortcuts': [
{
'name': '推荐',
'url': './ranking',
'icons': [
{
'src': './sparkles-icon-192x192.png',
'sizes': '192x192',
'type': 'image/png',
},
],
},
{
'name': '电影订阅',
'url': './subscribe-movie?tab=mysub',
'icons': [
{
'src': './clock-icon-192x192.png',
'sizes': '192x192',
'type': 'image/png',
},
],
},
{
'name': '电视剧订阅',
'url': './subscribe-tv?tab=mysub',
'icons': [
{
'src': './clock-icon-192x192.png',
'sizes': '192x192',
'type': 'image/png',
},
],
},
{
'name': '设置',
'url': './setting',
'icons': [
{
'src': './cog-icon-192x192.png',
'sizes': '192x192',
'type': 'image/png',
},
],
},
], ],
}, },
}), }),
@@ -63,8 +149,6 @@ export default defineConfig({
}, },
optimizeDeps: { optimizeDeps: {
exclude: ['vuetify'], exclude: ['vuetify'],
entries: [ entries: ['./src/**/*.vue'],
'./src/**/*.vue',
],
}, },
}) })
+832 -586
View File
File diff suppressed because it is too large Load Diff