mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-22 17:31:03 +01:00
加入版本检测
This commit is contained in:
23
backend/entity/version.go
Normal file
23
backend/entity/version.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package entity
|
||||
|
||||
type Release struct {
|
||||
Name string `json:"name"`
|
||||
Draft bool `json:"draft"`
|
||||
PreRelease bool `json:"pre_release"`
|
||||
PublishedAt string `json:"published_at"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type ReleaseSlices []Release
|
||||
|
||||
func (r ReleaseSlices) Len() int {
|
||||
return len(r)
|
||||
}
|
||||
|
||||
func (r ReleaseSlices) Less(i, j int) bool {
|
||||
return r[i].PublishedAt < r[j].PublishedAt
|
||||
}
|
||||
|
||||
func (r ReleaseSlices) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
@@ -133,7 +133,8 @@ func main() {
|
||||
anonymousGroup.PUT("/users", routes.PutUser) // 添加用户
|
||||
anonymousGroup.GET("/setting", routes.GetSetting) // 获取配置信息
|
||||
// release版本
|
||||
anonymousGroup.GET("/version", routes.GetVersion) // 获取发布的版本
|
||||
anonymousGroup.GET("/version", routes.GetVersion) // 获取发布的版本
|
||||
anonymousGroup.GET("/releases/latest", routes.GetLatestRelease) // 获取最近发布的版本
|
||||
}
|
||||
authGroup := app.Group("/", middlewares.AuthorizationMiddleware())
|
||||
{
|
||||
|
||||
20
backend/routes/version.go
Normal file
20
backend/routes/version.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"crawlab/services"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetLatestRelease(c *gin.Context) {
|
||||
latestRelease, err := services.GetLatestRelease()
|
||||
if err != nil {
|
||||
HandleError(http.StatusInternalServerError, c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, Response{
|
||||
Status: "ok",
|
||||
Message: "success",
|
||||
Data: latestRelease,
|
||||
})
|
||||
}
|
||||
29
backend/services/version.go
Normal file
29
backend/services/version.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crawlab/entity"
|
||||
"github.com/apex/log"
|
||||
"github.com/imroc/req"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func GetLatestRelease() (release entity.Release, err error) {
|
||||
res, err := req.Get("https://api.github.com/repos/crawlab-team/crawlab/releases")
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
debug.PrintStack()
|
||||
return release, err
|
||||
}
|
||||
|
||||
var releaseDataList entity.ReleaseSlices
|
||||
if err := res.ToJSON(&releaseDataList); err != nil {
|
||||
log.Errorf(err.Error())
|
||||
debug.PrintStack()
|
||||
return release, err
|
||||
}
|
||||
|
||||
sort.Sort(releaseDataList)
|
||||
|
||||
return releaseDataList[len(releaseDataList)-1], nil
|
||||
}
|
||||
@@ -35,33 +35,6 @@ export default {
|
||||
},
|
||||
methods: {},
|
||||
async mounted () {
|
||||
// window.setUseStats = (value) => {
|
||||
// document.querySelector('.el-message__closeBtn').click()
|
||||
// if (value === 1) {
|
||||
// this.$st.sendPv('/allow_stats')
|
||||
// this.$st.sendEv('全局', '允许/禁止统计', '允许')
|
||||
// } else {
|
||||
// this.$st.sendPv('/disallow_stats')
|
||||
// this.$st.sendEv('全局', '允许/禁止统计', '禁止')
|
||||
// }
|
||||
// localStorage.setItem('useStats', value)
|
||||
// }
|
||||
|
||||
// first-time user
|
||||
// if (this.useStats === undefined || this.useStats === null) {
|
||||
// this.$message({
|
||||
// type: 'info',
|
||||
// dangerouslyUseHTMLString: true,
|
||||
// showClose: true,
|
||||
// duration: 0,
|
||||
// message: '<p>' + this.$t('Do you allow us to collect some statistics to improve Crawlab?') + '</p>' +
|
||||
// '<div style="text-align: center;margin-top: 10px;">' +
|
||||
// '<button class="message-btn" onclick="setUseStats(1)">' + this.$t('Yes') + '</button>' +
|
||||
// '<button class="message-btn" onclick="setUseStats(0)">' + this.$t('No') + '</button>' +
|
||||
// '</div>'
|
||||
// })
|
||||
// }
|
||||
|
||||
// set uid if first visit
|
||||
if (this.uid === undefined || this.uid === null) {
|
||||
localStorage.setItem('uid', this.$utils.encrypt.UUID())
|
||||
@@ -71,6 +44,9 @@ export default {
|
||||
if (this.sid === undefined || this.sid === null) {
|
||||
sessionStorage.setItem('sid', this.$utils.encrypt.UUID())
|
||||
}
|
||||
|
||||
// get latest version
|
||||
await this.$store.dispatch('version/getLatestRelease')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<el-dialog
|
||||
:visible.sync="isLatestReleaseNoteVisible"
|
||||
:title="$t('Release') + ` ${latestRelease.name}`"
|
||||
>
|
||||
<el-tabs v-model="activeTabName">
|
||||
<el-tab-pane :label="$t('Release Note')" name="release-note">
|
||||
<div class="content markdown-body" v-html="latestReleaseNoteHtml">
|
||||
</div>
|
||||
<template slot="footer">
|
||||
<el-button type="primary" size="small" @click="isLatestReleaseNoteVisible = false">{{$t('Ok')}}</el-button>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('How to Upgrade')" name="how-to-upgrade">
|
||||
<div class="content markdown-body" v-html="howToUpgradeHtml">
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template slot="footer">
|
||||
<el-button type="primary" size="small" @click="isLatestReleaseNoteVisible = false">{{$t('Ok')}}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<hamburger :toggle-click="toggleSideBar" :is-active="sidebar.opened" class="hamburger-container"/>
|
||||
<breadcrumb class="breadcrumb"/>
|
||||
<el-dropdown class="avatar-container right" trigger="click">
|
||||
@@ -33,6 +55,12 @@
|
||||
<span style="margin-left: 5px;">{{$t('Documentation')}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="isUpgradable" class="upgrade right" @click="onClickUpgrade">
|
||||
<font-awesome-icon :icon="['fas', 'arrow-up']"/>
|
||||
<el-badge is-dot>
|
||||
<span style="margin-left: 5px;">{{$t('Upgrade')}}</span>
|
||||
</el-badge>
|
||||
</div>
|
||||
<div class="github right">
|
||||
<!-- Place this tag where you want the button to render. -->
|
||||
<github-button
|
||||
@@ -50,10 +78,15 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import {
|
||||
mapState,
|
||||
mapGetters
|
||||
} from 'vuex'
|
||||
import Breadcrumb from '@/components/Breadcrumb'
|
||||
import Hamburger from '@/components/Hamburger'
|
||||
import GithubButton from 'vue-github-button'
|
||||
import showdown from 'showdown'
|
||||
import 'github-markdown-css/github-markdown.css'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -61,7 +94,58 @@ export default {
|
||||
Hamburger,
|
||||
GithubButton
|
||||
},
|
||||
data () {
|
||||
const converter = new showdown.Converter()
|
||||
return {
|
||||
isLatestReleaseNoteVisible: false,
|
||||
converter,
|
||||
activeTabName: 'release-note',
|
||||
howToUpgradeHtmlZh: `
|
||||
### Docker 部署
|
||||
\`\`\`bash
|
||||
# 拉取最新镜像
|
||||
docker pull tikazyq/crawlab:latest
|
||||
|
||||
# 删除容器
|
||||
docker-compose down | true
|
||||
|
||||
# 启动容器
|
||||
docker-compose up -d
|
||||
\`\`\`
|
||||
|
||||
### 直接部署
|
||||
|
||||
1. 拉取最新 Github 代码
|
||||
2. 重新构建前后端应用
|
||||
3. 启动前后端应用
|
||||
`,
|
||||
howToUpgradeHtmlEn: `
|
||||
### Docker Deployment
|
||||
\`\`\`bash
|
||||
# pull the latest image
|
||||
docker pull tikazyq/crawlab:latest
|
||||
|
||||
# delete containers
|
||||
docker-compose down | true
|
||||
|
||||
# start containers
|
||||
docker-compose up -d
|
||||
\`\`\`
|
||||
|
||||
### Direct Deployment
|
||||
1. Pull the latest Github repository
|
||||
2. Build frontend and backend applications
|
||||
3. Start frontend and backend applications
|
||||
`
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('version', [
|
||||
'latestRelease'
|
||||
]),
|
||||
...mapState('lang', [
|
||||
'lang'
|
||||
]),
|
||||
...mapGetters([
|
||||
'sidebar',
|
||||
'avatar'
|
||||
@@ -70,6 +154,39 @@ export default {
|
||||
if (!this.$store.getters['user/userInfo']) return this.$t('User')
|
||||
if (!this.$store.getters['user/userInfo'].username) return this.$t('User')
|
||||
return this.$store.getters['user/userInfo'].username
|
||||
},
|
||||
isUpgradable () {
|
||||
if (!this.latestRelease.name) return false
|
||||
|
||||
const currentVersion = sessionStorage.getItem('v')
|
||||
const latestVersion = this.latestRelease.name.replace('v', '')
|
||||
|
||||
if (!latestVersion || !currentVersion) return false
|
||||
|
||||
const currentVersionList = currentVersion.split('.')
|
||||
const latestVersionList = latestVersion.split('.')
|
||||
for (let i = 0; i < currentVersionList.length; i++) {
|
||||
let nc = Number(currentVersionList[i])
|
||||
let nl = Number(latestVersionList[i])
|
||||
if (isNaN(nl)) nl = 0
|
||||
if (nc < nl) return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
latestReleaseNoteHtml () {
|
||||
if (!this.latestRelease.body) return ''
|
||||
const body = this.latestRelease.body
|
||||
return this.converter.makeHtml(body)
|
||||
},
|
||||
howToUpgradeHtml () {
|
||||
if (this.lang === 'zh') {
|
||||
console.log(this.howToUpgradeHtmlZh)
|
||||
return this.converter.makeHtml(this.howToUpgradeHtmlZh)
|
||||
} else if (this.lang === 'en') {
|
||||
return this.converter.makeHtml(this.howToUpgradeHtmlEn)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -86,6 +203,10 @@ export default {
|
||||
this.$store.commit('lang/SET_LANG', lang)
|
||||
|
||||
this.$st.sendEv('全局', '切换中英文', lang)
|
||||
},
|
||||
onClickUpgrade () {
|
||||
this.isLatestReleaseNoteVisible = true
|
||||
this.$st.sendEv('全局', '点击版本升级')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,6 +253,7 @@ export default {
|
||||
margin-right: 35px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
|
||||
.span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
@@ -143,8 +265,26 @@ export default {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.upgrade {
|
||||
margin-top: 12.5px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-right: 35px;
|
||||
|
||||
.span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.navbar >>> .el-dialog__body {
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user