mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-12-02 23:06:11 +01:00
70cf3e76b8
- 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 e68aaa0c16
)
144 lines
3.6 KiB
Go
144 lines
3.6 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
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)
|
|
}
|