加入版本检测

This commit is contained in:
marvzhang
2020-02-19 13:16:46 +08:00
parent 2b084d8670
commit e4c012cf2f
6 changed files with 218 additions and 29 deletions

23
backend/entity/version.go Normal file
View 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]
}

View File

@@ -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
View 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,
})
}

View 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
}

View File

@@ -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>

View File

@@ -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>