Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion extensions/package-vulnerability-scanner/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
client = connect.Client()

@app.get("/api/content")
async def search_content(query: str = None):
async def search_content(show_all: bool = False):
if show_all:
return client.content.find()
return client.me.content.find()

@app.get("/api/packages/{guid}")
Expand Down
2 changes: 1 addition & 1 deletion extensions/package-vulnerability-scanner/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@
"requiredFeatures": [
"API Publishing"
],
"version": "2.0.2"
"version": "3.0.2"
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script setup lang="ts">
import { computed, ref } from "vue";
import { computed, ref, watch } from "vue";
import { storeToRefs } from "pinia";

import { usePackagesStore } from "../stores/packages";
import { useContentStore } from "../stores/content";
import { useScannerStore } from "../stores/scanner";
import { useUserStore } from "../stores/user";
import type { User } from "../stores/user";
import StatusMessage from "./ui/StatusMessage.vue";
import SkeletonText from "./ui/SkeletonText.vue";
Expand All @@ -18,10 +20,22 @@ const props = defineProps<{
user: User;
}>();

const userStore = useUserStore();

const packagesStore = usePackagesStore();
const contentStore = useContentStore();
const scannerStore = useScannerStore();

// Use store's showAllContent to persist state across remounts
const { showAllContent } = storeToRefs(contentStore);

// Watch for toggle changes and refetch content
watch(showAllContent, async () => {
packagesStore.clearAllPackages();
await contentStore.fetchContentList(true);
fetchPackagesInBatches();
});

// Fetch packages in batches to avoid overwhelming the server
async function fetchPackagesInBatches(batchSize = 3) {
const contentToFetch = contentStore.contentList.filter(
Expand Down Expand Up @@ -152,6 +166,10 @@ const filteredContent = computed(() => {
});

const userHeader = computed(() => {
if (showAllContent.value) {
return "All Content on Connect";
}

let name;

if (props.user.first_name && props.user.last_name) {
Expand All @@ -166,7 +184,7 @@ const userHeader = computed(() => {
name = props.user.username;
}

return `${name}'s Connect Content`;
return `${name}'s Content on Connect`;
});
</script>

Expand All @@ -189,9 +207,25 @@ const userHeader = computed(() => {
</div>

<div v-else class="space-y-4">
<h2 class="text-xl font-semibold text-gray-800">
{{ userHeader }}
</h2>
<div class="flex items-center justify-between">
<h2 class="text-xl font-semibold text-gray-800">
{{ userHeader }}
</h2>

<label v-if="userStore.isAdmin" class="flex items-center gap-2 cursor-pointer">
<span class="text-sm text-gray-600">show:</span>
<span class="text-sm" :class="showAllContent ? 'text-gray-600' : 'text-gray-800 font-medium'">only mine</span>
<div class="relative">
<input
type="checkbox"
v-model="showAllContent"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</div>
<span class="text-sm" :class="showAllContent ? 'text-gray-800 font-medium' : 'text-gray-600'">all</span>
</label>
Comment on lines +215 to +227
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice, but not necessary to break this into a separate component.

</div>

<p class="text-gray-600">
Found {{ scannerStore.content.length }} content items with
Expand Down
13 changes: 8 additions & 5 deletions extensions/package-vulnerability-scanner/src/stores/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,22 @@ export const useContentStore = defineStore("content", () => {
const contentList = ref<ContentListItem[]>([]);
const isLoading = ref(false);
const error = ref<Error | null>(null);
const showAllContent = ref(false);

// Track if content has been loaded
// Track if content has been loaded for the current mode
const isContentLoaded = ref(false);

// Fetch all available content items
async function fetchContentList() {
// Skip if content is already loaded
if (isContentLoaded.value && contentList.value.length > 0) return;
async function fetchContentList(forceRefresh: boolean = false) {
// Skip if content is already loaded for this mode (unless forcing refresh)
if (!forceRefresh && isContentLoaded.value && contentList.value.length > 0) return;
Comment on lines +29 to +31
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this it was a bit jarring to flip back and forth and have the entire UI reload. I wonder if we could store the content separately so once loaded a use can swap back and forth.

Definitely too big for this PR though since that would require a substantial refactor.


isLoading.value = true;
error.value = null;

try {
const response = await fetch("api/content");
const url = showAllContent.value ? "api/content?show_all=true" : "api/content";
const response = await fetch(url);

if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
Expand All @@ -57,6 +59,7 @@ export const useContentStore = defineStore("content", () => {
isLoading,
error,
isContentLoaded,
showAllContent,

// Actions
fetchContentList,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export const usePackagesStore = defineStore("packages", () => {
};
}

function clearAllPackages() {
contentItems.value = {};
}

return {
// State
contentItems,
Expand All @@ -88,5 +92,6 @@ export const usePackagesStore = defineStore("packages", () => {
// Actions
fetchPackagesForContent,
setPackagesForContent,
clearAllPackages,
};
});
5 changes: 4 additions & 1 deletion extensions/package-vulnerability-scanner/src/stores/user.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ref } from "vue";
import { ref, computed } from "vue";
import { defineStore } from "pinia";

export interface User {
Expand All @@ -18,6 +18,8 @@ export interface User {
export const useUserStore = defineStore("user", () => {
const user = ref<User>();

const isAdmin = computed(() => user.value?.user_role === "administrator");

async function fetchCurrentUser() {
const response = await fetch("api/user");
const data = await response.json();
Expand All @@ -27,6 +29,7 @@ export const useUserStore = defineStore("user", () => {

return {
user,
isAdmin,

fetchCurrentUser,
};
Expand Down