forked from NYANDEV/forgejo
b4b586f41d
- Use TXT records in order to determine the latest available version. - This addresses a valid privacy issue, as with HTTP requests the server can keep track(estimated) of how many instances are using Forgejo, with DNS that's basically not possible as the server will never receive any data, as the only ones receiving data are DNS resolvers. (cherry picked from commit 0baefb546ab96bc3c06d90feffdb14873c2c2a3a) (cherry picked from commit e8ee41880b775532e6a68bd2052ed96d369dee78) (cherry picked from commit 7eca4f3bf1faa3f063c9668d1bb354b842361007) (cherry picked from commit 5c1567836c39b08b982005048f2610c3ad6d029a) (cherry picked from commit 953afbc67f85f7c15a064405f83b9973273d4d7b) (cherry picked from commitfd9d97ab9f
) (cherry picked from commit 40fbd45effed8e37c0d23dec4896473a437fb7eb) (cherry picked from commitc5c904b04b
) (cherry picked from commit48659bb3ab
) (cherry picked from commitb1fccd5093
) (cherry picked from commit5e69573860
)
142 lines
3.6 KiB
Go
142 lines
3.6 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package updatechecker
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/json"
|
|
"code.gitea.io/gitea/modules/proxy"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/system"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
)
|
|
|
|
// CheckerState stores the remote version from the JSON endpoint
|
|
type CheckerState struct {
|
|
LatestVersion string
|
|
}
|
|
|
|
// Name returns the name of the state item for update checker
|
|
func (r *CheckerState) Name() string {
|
|
return "update-checker"
|
|
}
|
|
|
|
// GiteaUpdateChecker returns error when new version of Gitea is available
|
|
func GiteaUpdateChecker(httpEndpoint, domainEndpoint string) error {
|
|
var version string
|
|
var err error
|
|
if domainEndpoint != "" {
|
|
version, err = getVersionDNS(domainEndpoint)
|
|
} else {
|
|
version, err = getVersionHTTP(httpEndpoint)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return UpdateRemoteVersion(version)
|
|
}
|
|
|
|
// getVersionDNS will request the TXT records for the domain. If a record starts
|
|
// with "forgejo_versions=" everything after that will be used as the latest
|
|
// version available.
|
|
func getVersionDNS(domainEndpoint string) (version string, err error) {
|
|
records, err := net.LookupTXT(domainEndpoint)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(records) == 0 {
|
|
return "", errors.New("no TXT records were found")
|
|
}
|
|
|
|
for _, record := range records {
|
|
if strings.HasPrefix(record, "forgejo_versions=") {
|
|
// Get all supported versions, separated by a comma.
|
|
supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",")
|
|
// For now always return the latest supported version.
|
|
return supportedVersions[len(supportedVersions)-1], nil
|
|
}
|
|
}
|
|
|
|
return "", errors.New("there is no TXT record with a valid value")
|
|
}
|
|
|
|
// getVersionHTTP will make an HTTP request to the endpoint, and the returned
|
|
// content is JSON. The "latest.version" path's value will be used as the latest
|
|
// version available.
|
|
func getVersionHTTP(httpEndpoint string) (version string, err error) {
|
|
httpClient := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: proxy.Proxy(),
|
|
},
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", httpEndpoint, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
type respType struct {
|
|
Latest struct {
|
|
Version string `json:"version"`
|
|
} `json:"latest"`
|
|
}
|
|
respData := respType{}
|
|
err = json.Unmarshal(body, &respData)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return respData.Latest.Version, nil
|
|
}
|
|
|
|
// UpdateRemoteVersion updates the latest available version of Gitea
|
|
func UpdateRemoteVersion(version string) (err error) {
|
|
return system.AppState.Set(&CheckerState{LatestVersion: version})
|
|
}
|
|
|
|
// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
|
|
func GetRemoteVersion() string {
|
|
item := new(CheckerState)
|
|
if err := system.AppState.Get(item); err != nil {
|
|
return ""
|
|
}
|
|
return item.LatestVersion
|
|
}
|
|
|
|
// GetNeedUpdate returns true whether a newer version of Gitea is available
|
|
func GetNeedUpdate() bool {
|
|
curVer, err := version.NewVersion(setting.AppVer)
|
|
if err != nil {
|
|
// return false to fail silently
|
|
return false
|
|
}
|
|
remoteVerStr := GetRemoteVersion()
|
|
if remoteVerStr == "" {
|
|
// no remote version is known
|
|
return false
|
|
}
|
|
remoteVer, err := version.NewVersion(remoteVerStr)
|
|
if err != nil {
|
|
// return false to fail silently
|
|
return false
|
|
}
|
|
return curVer.LessThan(remoteVer)
|
|
}
|