From c3fd98311c8464105d6d341001539827f0a7cdbc Mon Sep 17 00:00:00 2001 From: cade Date: Wed, 8 Jul 2026 12:40:19 +0800 Subject: [PATCH 01/15] WIP: Added VulnerablePackageBanner and moved shared logic to composable. Improved background/text clarity --- quasar.config.ts | 14 ++++++++++- .../banner/VulnerablePackageBanner.vue | 13 ++++++++++ .../VulnerablePackageComposable.ts | 25 +++++++++++++++++++ src/components/views/LocalModList.vue | 5 ++++ .../views/LocalModList/LocalModCard.vue | 23 +++++++++++++++-- src/css/scheme/dark.scss | 3 +++ src/css/scheme/light.scss | 2 ++ src/css/v2/components/_card.scss | 14 +++++++++++ 8 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 src/components/banner/VulnerablePackageBanner.vue create mode 100644 src/components/composables/VulnerablePackageComposable.ts diff --git a/quasar.config.ts b/quasar.config.ts index 4b6449be1..fe9e559da 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -1,6 +1,7 @@ // Configuration for your app // https://v2.quasar.dev/quasar-cli-vite/quasar-config-file +import path from 'node:path'; import { defineConfig } from '#q-app/wrappers'; export default defineConfig((ctx) => { @@ -47,7 +48,13 @@ export default defineConfig((ctx) => { typescript: { strict: true, vueShim: true, - // extendTsConfig (tsConfig) {} + extendTsConfig(tsConfig) { + tsConfig.compilerOptions.paths = { + '@r2': ['../src'], + '@r2/*': ['../src/*'], + ...tsConfig.compilerOptions.paths, + }; + } }, vueRouterMode: 'history', // available values: 'hash', 'history' @@ -71,6 +78,11 @@ export default defineConfig((ctx) => { extendViteConf (viteConf) { // Force Vite to use esbuild for CSS, overriding any defaults viteConf.build!.cssMinify = 'esbuild'; + + // Allow @r2 alias + viteConf.resolve ??= {}; + viteConf.resolve.alias ??= {}; + (viteConf.resolve.alias as Record)['@r2'] = path.resolve(__dirname, 'src'); }, viteVuePluginOptions: { template: { diff --git a/src/components/banner/VulnerablePackageBanner.vue b/src/components/banner/VulnerablePackageBanner.vue new file mode 100644 index 000000000..53bb3a4b6 --- /dev/null +++ b/src/components/banner/VulnerablePackageBanner.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/src/components/composables/VulnerablePackageComposable.ts b/src/components/composables/VulnerablePackageComposable.ts new file mode 100644 index 000000000..3f41ec188 --- /dev/null +++ b/src/components/composables/VulnerablePackageComposable.ts @@ -0,0 +1,25 @@ +import { getStore } from '@r2/providers/generic/store/StoreProvider'; +import { computed } from 'vue'; +import ManifestV2 from '@r2/model/ManifestV2'; +import ThunderstoreMod from '@r2/model/ThunderstoreMod'; + +const store = getStore(); + +const localModList = computed(() => store.state.profile.modList); +const onlineModList = computed>(() => { + const mods: ThunderstoreMod[] = store.state.tsMods.mods; + return new Map(mods.map(value => [value.getFullName(), value])); +}); + +const vulnerablePackages = computed(() => { + return localModList.value.filter(value => !value.isOnlineSource() && onlineModList.value.has(value.getName())); +}); + +const hasVulnerablePackages = computed(() => vulnerablePackages.value.length > 0); + +export function useVulnerablePackageComposable() { + return { + vulnerablePackages, + hasVulnerablePackages + } +} diff --git a/src/components/views/LocalModList.vue b/src/components/views/LocalModList.vue index 2da519543..3650a41a1 100644 --- a/src/components/views/LocalModList.vue +++ b/src/components/views/LocalModList.vue @@ -8,6 +8,7 @@ + @@ -37,9 +38,13 @@ import { State } from '../../store'; import { computed, defineAsyncComponent } from 'vue'; import SkeletonLocalModCard from './LocalModList/SkeletonLocalModCard.vue'; import ManagerUpdateBanner from '../banner/ManagerUpdateBanner.vue'; +import VulnerablePackageBanner from '@r2/components/banner/VulnerablePackageBanner.vue'; +import { useVulnerablePackageComposable } from '@r2/components/composables/VulnerablePackageComposable'; const store = getStore(); +const { hasVulnerablePackages } = useVulnerablePackageComposable(); + const LocalModDraggableList = defineAsyncComponent(() => import('./LocalModList/LocalModDraggableList.vue')); const visibleModList = computed(() => store.getters['profile/visibleModList']); diff --git a/src/components/views/LocalModList/LocalModCard.vue b/src/components/views/LocalModList/LocalModCard.vue index ec48152cf..e3f98be24 100644 --- a/src/components/views/LocalModList/LocalModCard.vue +++ b/src/components/views/LocalModList/LocalModCard.vue @@ -16,6 +16,7 @@ import { State } from '../../../store'; import { UnsatisfiedDependencies } from '../../../store/modules/ProfileModule'; import ThunderstoreMod from "../../../model/ThunderstoreMod"; import ThunderstoreVersion from "../../../model/ThunderstoreVersion"; +import { useVulnerablePackageComposable } from '@r2/components/composables/VulnerablePackageComposable'; const store = getStore(); @@ -26,6 +27,10 @@ type LocalModCardProps = { const props = defineProps(); +const { vulnerablePackages } = useVulnerablePackageComposable(); + +const disabledDependencies = ref([]); +const missingDependencies = ref([]); const disableChangePending = ref(false); const icon = useModIcon(() => props.mod); @@ -37,6 +42,7 @@ const isDeprecated = computed(() => store.state.tsMods.deprecated.get(props.mod. const isLatestVersion = computed(() => store.getters['tsMods/isLatestVersion'](props.mod)); const localModList = computed(() => store.state.profile.modList); const tsMod = computed(() => store.getters['tsMods/tsMod'](props.mod)); +const isConcerningPackage = computed(() => vulnerablePackages.value.findIndex(value => value.getName() === props.mod.getName()) >= 0) const unsatisfiedDependencies = computed(() => store.getters['profile/unsatisfiedDependencies'].get(props.mod.getName()) @@ -160,7 +166,8 @@ function getReadableDate(value: number): string { :enabled="mod.isEnabled()" :id="`${mod.getAuthorName()}-${mod.getName()}-${mod.getVersionNumber()}`" :image="icon" - :allowSorting="true"> + :allowSorting="true" + :class="[{'card--is-concern': isConcerningPackage}]">