feat: refresh a single repository from its own button (#2)

* feat(refresh): refresh a single repository with POST /refresh?repo=

The endpoint refreshes one configured repository and returns the full state plus the repo name.
Each repository has a 60-second cooldown that is separate from the full-refresh limit.
A running full refresh or a refresh of the same repository returns 409.
An unknown repository returns 404. Cross-site and disabled-config behaviour stay the same.
The collector reuses its fetch path and replaces only that repository.

* feat(ui): add a refresh button beside each repository

The button appears in the project list and in each attention group header.
It stays hidden when the server disables manual refresh.
The button shows a spinner while its repository refreshes and reports refusals in the notice line.
The store swaps in the returned state, so no extra fetch follows.
This commit is contained in:
2026-10-02 23:16:22 +01:00
committed by GitHub
parent 4c2c9d3341
commit 8cbc495dc7
16 changed files with 760 additions and 19 deletions
+18 -2
View File
@@ -2,10 +2,19 @@
import { faTag } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import AttentionRow from '@/components/AttentionRow.vue'
import RepoRefreshButton from '@/components/RepoRefreshButton.vue'
import { Badge } from '@/components/ui/badge'
import type { Block } from '@/lib/types'
defineProps<{ block: Block; nowMs: number; critSeconds: number }>()
defineProps<{
block: Block
nowMs: number
critSeconds: number
manualRefresh?: boolean
refreshing?: string[]
scanning?: boolean
}>()
defineEmits<{ refresh: [fullName: string] }>()
</script>
<template>
@@ -24,8 +33,15 @@ defineProps<{ block: Block; nowMs: number; critSeconds: number }>()
{{ block.repo.release.tag }}
</a>
</Badge>
<span class="text-muted-foreground ml-auto text-xs">
<span class="text-muted-foreground ml-auto flex items-center gap-1 text-xs">
{{ block.rows.length }} {{ block.rows.length === 1 ? 'item' : 'items' }}
<RepoRefreshButton
v-if="manualRefresh"
:repo="block.repo.fullName"
:busy="refreshing?.includes(block.repo.fullName) ?? false"
:blocked="scanning ?? false"
@refresh="$emit('refresh', block.repo.fullName)"
/>
</span>
</header>
<ul class="divide-border divide-y px-4">