mirror of
https://github.com/Luzifer/go-latestver.git
synced 2024-12-20 10:31:16 +00:00
Implement catalog entry view and badge redirect
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
fe63b10e21
commit
230d9211ee
6 changed files with 256 additions and 26 deletions
35
api.go
35
api.go
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -56,6 +57,40 @@ func catalogEntryToAPICatalogEntry(ce database.CatalogEntry) (APICatalogEntry, e
|
|||
return APICatalogEntry{CatalogEntry: ce, CatalogMeta: *cm}, nil
|
||||
}
|
||||
|
||||
func handleBadgeRedirect(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
compare = r.FormValue("compare")
|
||||
vars = mux.Vars(r)
|
||||
name, tag = vars["name"], vars["tag"]
|
||||
)
|
||||
|
||||
ce, err := configFile.CatalogEntryByTag(name, tag)
|
||||
if errors.Is(err, config.ErrCatalogEntryNotFound) {
|
||||
http.Error(w, "Not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
cm, err := storage.Catalog.GetMeta(&ce)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to fetch catalog data", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
color := "green"
|
||||
if compare != "" && compare != cm.CurrentVersion {
|
||||
color = "red"
|
||||
}
|
||||
|
||||
target, err := url.Parse(cfg.BadgeGenInstance)
|
||||
if err != nil {
|
||||
http.Error(w, "Misconfigured BadgeGenInstance", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
target.Path = path.Join(target.Path, "static", ce.Key(), cm.CurrentVersion, color)
|
||||
http.Redirect(w, r, target.String(), http.StatusFound)
|
||||
}
|
||||
|
||||
func handleCatalogGet(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
vars = mux.Vars(r)
|
||||
|
|
2
main.go
2
main.go
|
@ -18,6 +18,7 @@ import (
|
|||
|
||||
var (
|
||||
cfg = struct {
|
||||
BadgeGenInstance string `flag:"badge-gen-instance" default:"https://badges.fyi/" description:"Where to find the badge-gen instance to use badges from"`
|
||||
BaseURL string `flag:"base-url" default:"https://example.com/" description:"Base-URL the application is reachable at"`
|
||||
Config string `flag:"config,c" default:"config.yaml" description:"Configuration file with catalog entries"`
|
||||
Listen string `flag:"listen" default:":3000" description:"Port/IP to listen on"`
|
||||
|
@ -84,6 +85,7 @@ func main() {
|
|||
router.HandleFunc("/v1/catalog/{name}/{tag}/version", handleCatalogGetVersion).Methods(http.MethodGet)
|
||||
router.HandleFunc("/v1/log", handleLog).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/{name}/{tag}.svg", handleBadgeRedirect).Methods(http.MethodGet).Name("catalog-entry-badge")
|
||||
router.HandleFunc("/{name}/{tag}/log.rss", handleLogFeed).Methods(http.MethodGet).Name("catalog-entry-rss")
|
||||
router.HandleFunc("/log.rss", handleLogFeed).Methods(http.MethodGet).Name("log-rss")
|
||||
|
||||
|
|
12
src/app.vue
12
src/app.vue
|
@ -35,6 +35,18 @@
|
|||
/>
|
||||
All Updates
|
||||
</b-nav-item>
|
||||
|
||||
<b-nav-item
|
||||
href="https://github.com/Luzifer/go-latestver"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
title="Go-LatestVer on Github"
|
||||
>
|
||||
<font-awesome-icon
|
||||
fixed-width
|
||||
:icon="['fab', 'github']"
|
||||
/>
|
||||
</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
|
|
155
src/catalog_entry.vue
Normal file
155
src/catalog_entry.vue
Normal file
|
@ -0,0 +1,155 @@
|
|||
<template>
|
||||
<div>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-card-group>
|
||||
<b-card header="Current Version">
|
||||
{{ entry.current_version }}<br>
|
||||
<small>{{ moment(entry.version_time).format('lll') }}</small>
|
||||
</b-card>
|
||||
<b-card header="Last Checked">
|
||||
{{ moment(entry.last_checked).format('lll') }}
|
||||
</b-card>
|
||||
<b-card header="Badges">
|
||||
<p class="text-center">
|
||||
Current Version:<br>
|
||||
<img
|
||||
class="clickable"
|
||||
:src="badgeURL"
|
||||
@click="copyURL"
|
||||
>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
Compare to Version:<br>
|
||||
<img
|
||||
class="clickable"
|
||||
:src="`${badgeURL}?compare=otherversion`"
|
||||
@click="copyURL"
|
||||
>
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<small>(Click badge to copy URL)</small>
|
||||
</p>
|
||||
</b-card>
|
||||
<b-card
|
||||
header="External Links"
|
||||
no-body
|
||||
>
|
||||
<b-list-group flush>
|
||||
<b-list-group-item
|
||||
v-for="link in entry.links"
|
||||
:key="link.name"
|
||||
:href="link.url"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<font-awesome-icon
|
||||
fixed-width
|
||||
:icon="iconClassesToIcon(link.icon_class)"
|
||||
/>
|
||||
{{ link.name }}
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-card>
|
||||
</b-card-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<b-row class="mt-3">
|
||||
<b-col>
|
||||
<log-table :logs="logs" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import LogTable from './logtable.vue'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
components: { LogTable },
|
||||
|
||||
computed: {
|
||||
badgeURL() {
|
||||
return `${window.location.href.split('?')[0]}.svg`
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
entry: {},
|
||||
logs: [],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
copyURL(evt) {
|
||||
navigator.clipboard.writeText(evt.target.attributes.src.value)
|
||||
.then(() => this.$bvToast.toast('URL copied to clipboard', {
|
||||
autoHideDelay: 2000,
|
||||
solid: true,
|
||||
title: 'Copy Badge URL',
|
||||
variant: 'success',
|
||||
}))
|
||||
.catch(() => this.$bvToast.toast('Something went wrong', {
|
||||
autoHideDelay: 2000,
|
||||
solid: true,
|
||||
title: 'Copy Badge URL',
|
||||
variant: 'danger',
|
||||
}))
|
||||
},
|
||||
|
||||
fetchEntry() {
|
||||
axios.get(`/v1/catalog/${this.$route.params.name}/${this.$route.params.tag}`)
|
||||
.then(resp => {
|
||||
this.entry = resp.data
|
||||
})
|
||||
},
|
||||
|
||||
fetchLog() {
|
||||
axios.get(`/v1/catalog/${this.$route.params.name}/${this.$route.params.tag}/log`)
|
||||
.then(resp => {
|
||||
this.logs = resp.data
|
||||
})
|
||||
},
|
||||
|
||||
iconClassesToIcon(ic) {
|
||||
let namespace = 'fas'
|
||||
let icon = ''
|
||||
|
||||
for (const c of ic.split(' ')) {
|
||||
if (c === 'fa-fw') {
|
||||
continue
|
||||
}
|
||||
|
||||
if (['fab', 'fas'].includes(c)) {
|
||||
namespace = c
|
||||
}
|
||||
|
||||
if (c.startsWith('fa-')) {
|
||||
icon = c.replace('fa-', '')
|
||||
}
|
||||
}
|
||||
|
||||
return [namespace, icon]
|
||||
},
|
||||
|
||||
moment,
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchEntry()
|
||||
this.fetchLog()
|
||||
},
|
||||
|
||||
name: 'GoLatestVerCatalogEntry',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
img.clickable {
|
||||
cursor:pointer;
|
||||
}
|
||||
</style>
|
30
src/log.vue
30
src/log.vue
|
@ -1,40 +1,20 @@
|
|||
<template>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-table
|
||||
:fields="fields"
|
||||
:items="logs"
|
||||
small
|
||||
striped
|
||||
>
|
||||
<template #cell(_key)="data">
|
||||
<router-link :to="{name: 'entry', params: { name:data.item.catalog_name, tag: data.item.catalog_tag }}">
|
||||
{{ data.item.catalog_name }}:{{ data.item.catalog_tag }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template #cell(timestamp)="data">
|
||||
{{ moment(data.item.timestamp).format('lll') }}
|
||||
</template>
|
||||
</b-table>
|
||||
<log-table :logs="logs" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import moment from 'moment'
|
||||
import LogTable from './logtable.vue'
|
||||
|
||||
export default {
|
||||
components: { LogTable },
|
||||
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{ key: '_key', label: 'Catalog Entry' },
|
||||
{ key: 'version_from', label: 'Version From' },
|
||||
{ key: 'version_to', label: 'Version To' },
|
||||
{ key: 'timestamp', label: 'Updated At' },
|
||||
],
|
||||
|
||||
logs: [],
|
||||
}
|
||||
},
|
||||
|
@ -46,8 +26,6 @@ export default {
|
|||
this.logs = resp.data
|
||||
})
|
||||
},
|
||||
|
||||
moment,
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
|
48
src/logtable.vue
Normal file
48
src/logtable.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<b-table
|
||||
:fields="fields"
|
||||
:items="logs"
|
||||
small
|
||||
striped
|
||||
>
|
||||
<template #cell(_key)="data">
|
||||
<router-link :to="{name: 'entry', params: { name:data.item.catalog_name, tag: data.item.catalog_tag }}">
|
||||
{{ data.item.catalog_name }}:{{ data.item.catalog_tag }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template #cell(timestamp)="data">
|
||||
{{ moment(data.item.timestamp).format('lll') }}
|
||||
</template>
|
||||
</b-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{ key: '_key', label: 'Catalog Entry' },
|
||||
{ key: 'version_from', label: 'Version From' },
|
||||
{ key: 'version_to', label: 'Version To' },
|
||||
{ key: 'timestamp', label: 'Updated At' },
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
moment,
|
||||
},
|
||||
|
||||
name: 'GoLatestVerLogTable',
|
||||
|
||||
props: {
|
||||
logs: {
|
||||
required: true,
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
Loading…
Reference in a new issue